apm.c revision 3264
1/* 2 * LP (Laptop Package) 3 * 4 * Copyright (c) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> 5 * 6 * This software may be used, modified, copied, and distributed, in 7 * both source and binary form provided that the above copyright and 8 * these terms are retained. Under no circumstances is the author 9 * responsible for the proper functioning of this software, nor does 10 * the author assume any responsibility for damages incurred with its 11 * use. 12 * 13 * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) 14 * 15 * $Id$ 16 */ 17 18#include "apm.h" 19 20#if NAPM > 0 21 22#include <sys/param.h> 23#include "conf.h" 24#include <sys/kernel.h> 25#include <sys/systm.h> 26#include <sys/malloc.h> 27#include <sys/ioctl.h> 28#include <sys/tty.h> 29#include <sys/file.h> 30#include <sys/proc.h> 31#include <sys/vnode.h> 32#include "i386/isa/isa.h" 33#include "i386/isa/isa_device.h" 34#include <machine/apm_bios.h> 35#include <machine/segments.h> 36#include <vm/vm.h> 37#include <sys/syslog.h> 38#include "apm_setup.h" 39 40/* static data */ 41static int apm_initialized = 0, active = 0, halt_cpu = 1; 42static u_int minorversion, majorversion; 43static u_int cs32_base, cs16_base, ds_base; 44static u_int cs_limit, ds_limit; 45static u_int cs_entry; 46static u_int intversion; 47static int idle_cpu, disabled, disengaged; 48 49/* Map version number to integer (keeps ordering of version numbers) */ 50#define INTVERSION(major, minor) ((major)*100 + (minor)) 51 52static timeout_t apm_timeout; 53 54/* setup APM GDT discriptors */ 55static void 56setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code_limit, u_int data_limit) 57{ 58 /* setup 32bit code segment */ 59 gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base; 60 gdt_segs[GAPMCODE32_SEL].ssd_limit = code_limit; 61 62 /* setup 16bit code segment */ 63 gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base; 64 gdt_segs[GAPMCODE16_SEL].ssd_limit = code_limit; 65 66 /* setup data segment */ 67 gdt_segs[GAPMDATA_SEL ].ssd_base = data_base; 68 gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit; 69 70 /* reflect these changes on physical GDT */ 71 ssdtosd(gdt_segs + GAPMCODE32_SEL, gdt + GAPMCODE32_SEL); 72 ssdtosd(gdt_segs + GAPMCODE16_SEL, gdt + GAPMCODE16_SEL); 73 ssdtosd(gdt_segs + GAPMDATA_SEL , gdt + GAPMDATA_SEL ); 74} 75 76/* 48bit far pointer */ 77struct addr48 { 78 u_long offset; 79 u_short segment; 80} apm_addr; 81 82 83/* register structure for BIOS call */ 84union real_regs { 85 struct xregs { 86 u_short ax; 87 u_short bx __attribute__ ((packed)); 88 u_short cx __attribute__ ((packed)); 89 u_short dx __attribute__ ((packed)); 90 u_short si __attribute__ ((packed)); 91 u_short di __attribute__ ((packed)); 92 u_short cf __attribute__ ((packed)); /* carry */ 93 } x; 94 struct hlregs { 95 u_char al; 96 u_char ah __attribute__ ((packed)); 97 u_char bl __attribute__ ((packed)); 98 u_char bh __attribute__ ((packed)); 99 u_char cl __attribute__ ((packed)); 100 u_char ch __attribute__ ((packed)); 101 u_char dl __attribute__ ((packed)); 102 u_char dh __attribute__ ((packed)); 103 u_short si __attribute__ ((packed)); 104 u_short di __attribute__ ((packed)); 105 u_short cf __attribute__ ((packed)); /* carry */ 106 } hl; 107}; 108 109 110/* call APM BIOS */ 111extern void call_apm(union real_regs* regs); 112 113extern u_char apm_errno; 114 115/* enable/disable power management */ 116static int 117apm_enable_disable_pm(int enable) 118{ 119 union real_regs regs; 120 121 regs.hl.ah = APM_BIOS; 122 regs.hl.al = APM_ENABLEDISABLEPM; 123 if (intversion >= INTVERSION(1, 1)) { 124 regs.x.bx = PMDV_ALLDEV; 125 } 126 else { 127 regs.x.bx = 0xffff; /* APM version 1.0 only */ 128 } 129 regs.x.cx = enable; 130 call_apm(®s); 131 return regs.x.cf; 132} 133 134/* engage/disengage power management (APM 1.1 or later) */ 135static int 136apm_engage_disengage_pm(int engage) 137{ 138 union real_regs regs; 139 140 regs.hl.ah = APM_BIOS; 141 regs.hl.al = APM_ENGAGEDISENGAGEPM; 142 regs.x.bx = PMDV_ALLDEV; 143 regs.x.cx = engage; 144 call_apm(®s); 145 return regs.x.cf; 146} 147 148/* get PM event */ 149static u_int 150apm_getevent(void) 151{ 152 union real_regs regs; 153 154 regs.hl.ah = APM_BIOS; 155 regs.hl.al = APM_GETPMEVENT; 156 call_apm(®s); 157 if (regs.x.cf) { 158#if 0 159 printf("No event: errcode = %d\n", apm_errno); 160#endif 161 return PMEV_NOEVENT; 162 } 163 return (u_int)regs.x.bx; 164} 165 166/* 167 * In many cases, the first event that occured after resume, needs 168 * special treatment. This binary flag make this process possible. 169 * Initial value of this variable is 1, because the bootstrap 170 * condition is equivalent to resumed condition for the power 171 * manager. 172 */ 173static int resumed_event = 1; 174 175/* suspend entire system */ 176static int 177apm_suspend_system(void) 178{ 179 union real_regs regs; 180 181 regs.hl.ah = APM_BIOS; 182 regs.hl.al = APM_SETPWSTATE; 183 regs.x.bx = PMDV_ALLDEV; 184 regs.x.cx = PMST_SUSPEND; 185 call_apm(®s); 186 if (regs.x.cf) { 187 printf("Entire system suspend failure: errcode = %d\n", apm_errno); 188 return 1; 189 } 190 resumed_event = 1; 191 192 return 0; 193} 194 195/* APM Battery low handler */ 196static void 197apm_battery_low(void) 198{ 199 /* Currently, this routine has not been implemented. Sorry... */ 200} 201 202 203/* APM driver calls some functions automatically when the system wakes up */ 204static void 205apm_execute_hook(apm_hook_func_t list) 206{ 207 apm_hook_func_t p; 208 209 for (p = list; p != NULL; p = p->next) { 210 if ((*(p->func))()) { 211 printf("Warning: APM hook of %s failed", p->name); 212 } 213 } 214} 215 216 217/* APM hook manager */ 218static apm_hook_func_t 219apm_hook_init(apm_hook_func_t *list, int (*func)(void), char *name, int order) 220{ 221 int pl; 222 apm_hook_func_t p, prev, new_node; 223 224 pl = splhigh(); 225 new_node = malloc(sizeof(*new_node), M_DEVBUF, M_NOWAIT); 226 if (new_node == NULL) { 227 panic("Can't allocate device buffer for apm_resume_hook."); 228 } 229 new_node->func = func; 230 new_node->name = name; 231#if 0 232 new_node->next = *list; 233 *list = new_node; 234#else 235 prev = NULL; 236 for (p = *list; p != NULL; prev = p, p = p->next) { 237 if (p->order > order) { 238 break; 239 } 240 } 241 242 if (prev == NULL) { 243 new_node->next = *list; 244 *list = new_node; 245 } 246 else { 247 new_node->next = prev->next; 248 prev->next = new_node; 249 } 250#endif 251 splx(pl); 252 return new_node; 253} 254 255void 256apm_hook_delete(apm_hook_func_t *list, apm_hook_func_t delete_node) 257{ 258 int pl; 259 apm_hook_func_t p, prev; 260 261 pl = splhigh(); 262 prev = NULL; 263 for (p = *list; p != NULL; prev = p, p = p->next) { 264 if (p == delete_node) { 265 goto deleteit; 266 } 267 } 268 panic("Tried to delete unregistered apm_resume_hook."); 269 goto nosuchnode; 270deleteit: 271 if (prev != NULL) { 272 prev->next = p->next; 273 } 274 else { 275 *list = p->next; 276 } 277 free(delete_node, M_DEVBUF); 278nosuchnode: 279 splx(pl); 280} 281 282static struct timeval suspend_time; 283 284/* default APM hook functions */ 285static int 286apm_default_resume(void) 287{ 288 u_int second, minute, hour; 289 struct timeval resume_time; 290 291 inittodr(0); /* adjust time to RTC */ 292 microtime(&resume_time); 293 second = resume_time.tv_sec - suspend_time.tv_sec; 294 hour = second / 3600; 295 second %= 3600; 296 minute = second / 60; 297 second %= 60; 298 log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", hour, minute, second); 299 return 0; 300} 301 302static int 303apm_default_suspend(void) 304{ 305 int pl; 306#if 0 307 pl = splhigh(); 308 sync(curproc, NULL, NULL); 309 splx(pl); 310#endif 311 microtime(&suspend_time); 312 return 0; 313} 314 315/* list structure for hook */ 316static apm_hook_func_t apm_resume_hook = NULL; 317static apm_hook_func_t apm_suspend_hook = NULL; 318 319/* execute resume hook */ 320static void 321apm_execute_resume_hook(void) 322{ 323 apm_execute_hook(apm_resume_hook); 324} 325 326/* add a node on resume hook */ 327apm_hook_func_t 328apm_resume_hook_init(int (*func)(void), char *name, int order) 329{ 330 return apm_hook_init(&apm_resume_hook, func, name, order); 331} 332 333/* delete a node from resume hook */ 334void 335apm_resume_hook_delete(apm_hook_func_t delete_node) 336{ 337 apm_hook_delete(&apm_resume_hook, delete_node); 338} 339 340/* execute suspend hook */ 341static void 342apm_execute_suspend_hook(void) 343{ 344 apm_execute_hook(apm_suspend_hook); 345} 346 347/* add a node on resume hook */ 348apm_hook_func_t 349apm_suspend_hook_init(int (*func)(void), char *name, int order) 350{ 351 return apm_hook_init(&apm_suspend_hook, func, name, order); 352} 353 354/* delete a node from resume hook */ 355void 356apm_suspend_hook_delete(apm_hook_func_t delete_node) 357{ 358 apm_hook_delete(&apm_suspend_hook, delete_node); 359} 360 361/* get APM information */ 362static int 363apm_get_info(apm_info_t aip) 364{ 365 union real_regs regs; 366 367 regs.hl.ah = APM_BIOS; 368 regs.hl.al = APM_GETPWSTATUS; 369 regs.x.bx = PMDV_ALLDEV; 370 call_apm(®s); 371 if (regs.x.cf) { 372 printf("Get APM info failure: errcode = %d\n", apm_errno); 373 return 1; 374 } 375 aip->ai_major = (u_int)majorversion; 376 aip->ai_minor = (u_int)minorversion; 377 aip->ai_acline = (u_int)regs.hl.bh; 378 aip->ai_batt_stat = (u_int)regs.hl.bl; 379 aip->ai_batt_life = (u_int)regs.hl.cl; 380 return 0; 381} 382 383 384/* Define equivalent event sets */ 385 386static int equiv_event_num = 0; 387static struct apm_eqv_event equiv_events[APM_MAX_EQUIV_EVENTS]; 388 389static int 390apm_def_eqv(apm_eqv_event_t aee) 391{ 392 if (equiv_event_num == APM_MAX_EQUIV_EVENTS) { 393 return 1; 394 } 395 memcpy(&equiv_events[equiv_event_num], aee, sizeof(struct apm_eqv_event)); 396 equiv_event_num++; 397 return 0; 398} 399 400static void 401apm_flush_eqv(void) 402{ 403 equiv_event_num = 0; 404} 405 406static void apm_processevent(void); 407 408/* 409 * Public interface to the suspend/resume: 410 * 411 * Execute suspend and resume hook before and after sleep, respectively. 412 */ 413 414void 415apm_suspend_resume(void) 416{ 417 int pl; 418#if 0 419 printf("Called apm_suspend_resume();\n"); 420#endif 421 if (apm_initialized) { 422 apm_execute_suspend_hook(); 423 apm_suspend_system(); 424 apm_execute_resume_hook(); 425 apm_processevent(); 426 } 427} 428 429/* inform APM BIOS that CPU is idle */ 430void 431apm_cpu_idle(void) 432{ 433 if (idle_cpu) { 434 if (active) { 435 asm("movw $0x5305, %ax; lcall _apm_addr"); 436 } 437 } 438 /* 439 * Some APM implementation halts CPU in BIOS, whenever 440 * "CPU-idle" function are invoked, but swtch() of 441 * FreeBSD halts CPU, therefore, CPU is halted twice 442 * in the sched loop. It makes the interrupt latency 443 * terribly long and be able to cause a serious problem 444 * in interrupt processing. We prevent it by removing 445 * "hlt" operation from swtch() and managed it under 446 * APM driver. 447 */ 448 if (!active || halt_cpu) { 449 asm("sti ; hlt"); /* wait for interrupt */ 450 } 451} 452 453/* inform APM BIOS that CPU is busy */ 454void 455apm_cpu_busy(void) 456{ 457 if (idle_cpu && active) { 458 asm("movw $0x5306, %ax; lcall _apm_addr"); 459 } 460} 461 462 463/* 464 * APM timeout routine: 465 * 466 * This routine is automatically called by timer two times within one 467 * seconed. 468 */ 469 470static void 471apm_timeout(void *arg1) 472{ 473#if 0 474 printf("Called apm_timeout\n"); 475#endif 476 apm_processevent(); 477 timeout(apm_timeout, NULL, hz / 2); /* 2 Hz */ 478 /* APM driver must polls APM event a time per second */ 479} 480 481/* enable APM BIOS */ 482static void 483apm_event_enable(void) 484{ 485#if 0 486 printf("called apm_event_enable()\n"); 487#endif 488 if (apm_initialized) { 489 active = 1; 490 timeout(apm_timeout, NULL, 2 * hz); 491 } 492} 493 494/* disable APM BIOS */ 495static void 496apm_event_disable(void) 497{ 498#if 0 499 printf("called apm_event_disable()\n"); 500#endif 501 if (apm_initialized) { 502 untimeout(apm_timeout, NULL); 503 active = 0; 504 } 505} 506 507/* halt CPU in scheduling loop */ 508static void apm_halt_cpu(void) 509{ 510 if (apm_initialized) { 511 halt_cpu = 1; 512 } 513} 514 515/* don't halt CPU in scheduling loop */ 516static void apm_not_halt_cpu(void) 517{ 518 if (apm_initialized) { 519 halt_cpu = 0; 520 } 521} 522 523/* device driver definitions */ 524int apmprobe (struct isa_device *); 525int apmattach(struct isa_device *); 526 527struct isa_driver apmdriver = { apmprobe, apmattach, "apm" }; 528 529 530/* 531 * probe APM (dummy): 532 * 533 * APM probing routine is placed on locore.s and apm_init.S because 534 * this process forces the CPU to turn to real mode or V86 mode. 535 * Current version uses real mode, but on future version, we want 536 * to use V86 mode in APM initialization. 537 */ 538int 539apmprobe(struct isa_device *dvp) 540{ 541 switch (apm_version) { 542 case APMINI_CANTFIND: 543 /* silent */ 544 return 0; 545 case APMINI_NOT32BIT: 546 printf("apm%d: 32bit connection is not supported.\n", dvp->id_unit); 547 return 0; 548 case APMINI_CONNECTERR: 549 printf("apm%d: 32-bit connection error.\n", dvp->id_unit); 550 return 0; 551 } 552 if ((apm_version & 0xff00) != 0x0100) return 0; 553 if ((apm_version & 0x00f0) >= 0x00a0) return 0; 554 if ((apm_version & 0x000f) >= 0x000a) return 0; 555 return -1; 556} 557 558static const char * 559is_enabled(int enabled) 560{ 561 if (enabled) { 562 return "enabled"; 563 } 564 return "disabled"; 565} 566 567static const char * 568apm_error(void) 569{ 570 static char buffer[64]; 571 572 switch (apm_errno) { 573 case 0: 574 return "APM OK."; 575 default: 576 sprintf(buffer, "Unknown Error 0x%x", (u_int)apm_errno); 577 return buffer; 578 } 579} 580 581 582 583/* Process APM event */ 584static void 585apm_processevent(void) 586{ 587 int i, apm_event; 588 589getevent: 590 while (1) { 591 if ((apm_event = apm_getevent()) == PMEV_NOEVENT) { 592 break; 593 } 594#if 0 595#if 1 596#define OPMEV_DEBUGMESSAGE(symbol) case symbol: break; 597#else 598#define OPMEV_DEBUGMESSAGE(symbol) case symbol: printf("Original APM Event: " #symbol "\n"); break 599#endif 600 switch (apm_event) { 601 OPMEV_DEBUGMESSAGE(PMEV_NOEVENT); 602 OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 603 OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 604 OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 605 OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 606 OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 607 OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 608 OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 609 OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 610 OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 611 OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 612 default: 613 printf("Unknown Original APM Event 0x%x\n", apm_event); 614 break; 615 } 616#endif 617 for (i = 0; i < equiv_event_num; i++) { 618 if (equiv_events[i].aee_event == apm_event) { 619 u_int tmp = PMEV_DEFAULT; 620 if (resumed_event) { 621 tmp = equiv_events[i].aee_resume; 622 } 623 else { 624 tmp = equiv_events[i].aee_equiv; 625 } 626 if (tmp != PMEV_DEFAULT) { 627 apm_event = tmp; 628 break; 629 } 630 } 631 } 632#if 1 633#if 1 634#define PMEV_DEBUGMESSAGE(symbol) case symbol: break; 635#else 636#define PMEV_DEBUGMESSAGE(symbol) case symbol: printf("APM Event: " #symbol "\n"); break 637#endif 638 switch (apm_event) { 639 PMEV_DEBUGMESSAGE(PMEV_NOEVENT); 640 PMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 641 PMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 642 PMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 643 PMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 644 PMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 645 PMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 646 PMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 647 PMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 648 PMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 649 PMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 650 default: 651 printf("Unknown APM Event 0x%x\n", apm_event); 652 break; 653 } 654#endif 655 switch (apm_event) { 656 case PMEV_NOEVENT: 657 case PMEV_STANDBYREQ: 658 case PMEV_POWERSTATECHANGE: 659 case PMEV_CRITSUSPEND: 660 case PMEV_USERSTANDBYREQ: 661 case PMEV_USERSUSPENDREQ: 662 break; 663 case PMEV_BATTERYLOW: 664 apm_battery_low(); 665 break; 666 case PMEV_SUSPENDREQ: 667 apm_suspend_resume(); 668 break; 669 case PMEV_NORMRESUME: 670 case PMEV_CRITRESUME: 671 case PMEV_UPDATETIME: 672 case PMEV_STANDBYRESUME: 673 inittodr(0); /* adjust time to RTC */ 674 break; 675 } 676 } 677 resumed_event = 0; 678} 679 680 681/* 682 * Attach APM: 683 * 684 * Initialize APM driver (APM BIOS itself has been initialized in locore.s) 685 */ 686 687int 688apmattach(struct isa_device *dvp) 689{ 690 691 /* setup APM parameters */ 692 minorversion = ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0); 693 majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8); 694 intversion = INTVERSION(majorversion, minorversion); 695 cs32_base = (apm_cs32_base << 4) + KERNBASE; 696 cs16_base = (apm_cs16_base << 4) + KERNBASE; 697 ds_base = (apm_ds_base << 4) + KERNBASE; 698 cs_limit = apm_cs_limit; 699 ds_limit = apm_ds_limit; 700 cs_entry = apm_cs_entry; 701 idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0); 702 disabled = ((apm_flags & APM_DISABLED) != 0); 703 disengaged = ((apm_flags & APM_DISENGAGED) != 0); 704 705 /* print bootstrap messages */ 706#ifdef DEBUG 707 printf(" found APM BIOS version %d.%d\n", dvp->id_unit, majorversion, minorversion); 708 printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", dvp->id_unit, cs32_base, cs16_base, ds_base); 709 printf("apm%d: Code entry 0x%08x, Idling CPU %s, Management %s\n", dvp->id_unit, cs_entry, is_enabled(idle_cpu), is_enabled(!disabled)); 710#else 711 printf(" found APM BIOS version %d.%d\n", majorversion, minorversion); 712 printf("apm%d: Idling CPU %s\n", dvp->id_unit, is_enabled(idle_cpu)); 713#endif 714 715 /* 716 * APM 1.0 does not have: 717 * 718 * 1. segment limit parameters 719 * 720 * 2. engage/disengage operations 721 */ 722 if (intversion >= INTVERSION(1, 1)) { 723 printf("apm%d: Engaged control %s\n", dvp->id_unit, is_enabled(!disengaged)); 724 } 725 else { 726 cs_limit = 0xffff; 727 ds_limit = 0xffff; 728 } 729 730 /* setup GDT */ 731 setup_apm_gdt(cs32_base, cs16_base, ds_base, cs_limit, ds_limit); 732 733 /* setup entry point 48bit pointer */ 734 apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); 735 apm_addr.offset = cs_entry; 736 737 /* enable power management */ 738 if (disabled) { 739 if (apm_enable_disable_pm(1)) { 740 printf("Warning: APM enable function failed! [%s]\n", apm_error()); 741 } 742 } 743 744 /* engage power managment (APM 1.1 or later) */ 745 if (intversion >= INTVERSION(1, 1) && disengaged) { 746 if (apm_engage_disengage_pm(1)) { 747 printf("Warning: APM engage function failed [%s]\n", apm_error()); 748 } 749 } 750 751 apm_suspend_hook_init(apm_default_suspend, "default suspend", APM_MAX_ORDER); 752 apm_resume_hook_init (apm_default_resume , "default resume" , APM_MIN_ORDER); 753 apm_initialized = 1; 754 755 return 0; 756} 757 758int 759apmopen(dev_t dev, int flag, int fmt, struct proc *p) 760{ 761 if (!apm_initialized) { 762 return ENXIO; 763 } 764 switch (minor(dev)) { 765 case 0: /* apm0 */ 766 break; 767 defaults: 768 return (ENXIO); 769 } 770 return 0; 771} 772 773int 774apmclose(dev_t dev, int flag, int fmt, struct proc *p) 775{ 776 return 0; 777} 778 779int 780apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) 781{ 782 int error = 0; 783 int pl; 784 785#if 0 786 printf("APM ioctl: minor = %d, cmd = 0x%x\n", minor(dev), cmd); 787#endif 788 789 pl = splhigh(); 790 if (minor(dev) != 0) { 791 return ENXIO; 792 } 793 if (!apm_initialized) { 794 return ENXIO; 795 } 796 switch (cmd) { 797 case APMIO_SUSPEND: 798 apm_suspend_resume(); 799 break; 800 case APMIO_GETINFO: 801 if (apm_get_info((apm_info_t)addr)) { 802 error = ENXIO; 803 } 804 break; 805 case APMIO_DEFEQV: 806 if (apm_def_eqv((apm_eqv_event_t)addr)) { 807 error = ENOSPC; 808 } 809 break; 810 case APMIO_FLUSHEQV: 811 apm_flush_eqv(); 812 break; 813 case APMIO_ENABLE: 814 apm_event_enable(); 815 break; 816 case APMIO_DISABLE: 817 apm_event_disable(); 818 break; 819 case APMIO_HALTCPU: 820 apm_halt_cpu(); 821 break; 822 case APMIO_NOTHALTCPU: 823 apm_not_halt_cpu(); 824 break; 825 default: 826 error = EINVAL; 827 break; 828 } 829 splx(pl); 830 return error; 831} 832 833#endif /* NAPM > 0 */ 834