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