apm.c revision 3258
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 return -1; 551} 552 553static const char * 554is_enabled(int enabled) 555{ 556 if (enabled) { 557 return "enabled"; 558 } 559 return "disabled"; 560} 561 562static const char * 563apm_error(void) 564{ 565 static char buffer[64]; 566 567 switch (apm_errno) { 568 case 0: 569 return "APM OK."; 570 default: 571 sprintf(buffer, "Unknown Error 0x%x", (u_int)apm_errno); 572 return buffer; 573 } 574} 575 576 577 578/* Process APM event */ 579static void 580apm_processevent(void) 581{ 582 int i, apm_event; 583 584getevent: 585 while (1) { 586 if ((apm_event = apm_getevent()) == PMEV_NOEVENT) { 587 break; 588 } 589#if 0 590#if 1 591#define OPMEV_DEBUGMESSAGE(symbol) case symbol: break; 592#else 593#define OPMEV_DEBUGMESSAGE(symbol) case symbol: printf("Original APM Event: " #symbol "\n"); break 594#endif 595 switch (apm_event) { 596 OPMEV_DEBUGMESSAGE(PMEV_NOEVENT); 597 OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 598 OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 599 OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 600 OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 601 OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 602 OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 603 OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 604 OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 605 OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 606 OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 607 default: 608 printf("Unknown Original APM Event 0x%x\n", apm_event); 609 break; 610 } 611#endif 612 for (i = 0; i < equiv_event_num; i++) { 613 if (equiv_events[i].aee_event == apm_event) { 614 u_int tmp = PMEV_DEFAULT; 615 if (resumed_event) { 616 tmp = equiv_events[i].aee_resume; 617 } 618 else { 619 tmp = equiv_events[i].aee_equiv; 620 } 621 if (tmp != PMEV_DEFAULT) { 622 apm_event = tmp; 623 break; 624 } 625 } 626 } 627#if 1 628#if 1 629#define PMEV_DEBUGMESSAGE(symbol) case symbol: break; 630#else 631#define PMEV_DEBUGMESSAGE(symbol) case symbol: printf("APM Event: " #symbol "\n"); break 632#endif 633 switch (apm_event) { 634 PMEV_DEBUGMESSAGE(PMEV_NOEVENT); 635 PMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 636 PMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 637 PMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 638 PMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 639 PMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 640 PMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 641 PMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 642 PMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 643 PMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 644 PMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 645 default: 646 printf("Unknown APM Event 0x%x\n", apm_event); 647 break; 648 } 649#endif 650 switch (apm_event) { 651 case PMEV_NOEVENT: 652 case PMEV_STANDBYREQ: 653 case PMEV_POWERSTATECHANGE: 654 case PMEV_CRITSUSPEND: 655 case PMEV_USERSTANDBYREQ: 656 case PMEV_USERSUSPENDREQ: 657 break; 658 case PMEV_BATTERYLOW: 659 apm_battery_low(); 660 break; 661 case PMEV_SUSPENDREQ: 662 apm_suspend_resume(); 663 break; 664 case PMEV_NORMRESUME: 665 case PMEV_CRITRESUME: 666 case PMEV_UPDATETIME: 667 case PMEV_STANDBYRESUME: 668 inittodr(0); /* adjust time to RTC */ 669 break; 670 } 671 } 672 resumed_event = 0; 673} 674 675 676/* 677 * Attach APM: 678 * 679 * Initialize APM driver (APM BIOS itself has been initialized in locore.s) 680 */ 681 682int 683apmattach(struct isa_device *dvp) 684{ 685 686 /* setup APM parameters */ 687 minorversion = ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0); 688 majorversion = ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8); 689 intversion = INTVERSION(majorversion, minorversion); 690 cs32_base = (apm_cs32_base << 4) + KERNBASE; 691 cs16_base = (apm_cs16_base << 4) + KERNBASE; 692 ds_base = (apm_ds_base << 4) + KERNBASE; 693 cs_limit = apm_cs_limit; 694 ds_limit = apm_ds_limit; 695 cs_entry = apm_cs_entry; 696 idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0); 697 disabled = ((apm_flags & APM_DISABLED) != 0); 698 disengaged = ((apm_flags & APM_DISENGAGED) != 0); 699 700 /* print bootstrap messages */ 701 printf(" found APM BIOS version %d.%d\n", dvp->id_unit, majorversion, minorversion); 702 printf("apm%d: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", dvp->id_unit, cs32_base, cs16_base, ds_base); 703 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)); 704 705 /* 706 * APM 1.0 does not have: 707 * 708 * 1. segment limit parameters 709 * 710 * 2. engage/disengage operations 711 */ 712 if (intversion >= INTVERSION(1, 1)) { 713 printf("apm%d: Engaged control %s\n", dvp->id_unit, is_enabled(!disengaged)); 714 } 715 else { 716 cs_limit = 0xffff; 717 ds_limit = 0xffff; 718 } 719 720 /* setup GDT */ 721 setup_apm_gdt(cs32_base, cs16_base, ds_base, cs_limit, ds_limit); 722 723 /* setup entry point 48bit pointer */ 724 apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); 725 apm_addr.offset = cs_entry; 726 727 /* enable power management */ 728 if (disabled) { 729 if (apm_enable_disable_pm(1)) { 730 printf("Warning: APM enable function failed! [%s]\n", apm_error()); 731 } 732 } 733 734 /* engage power managment (APM 1.1 or later) */ 735 if (intversion >= INTVERSION(1, 1) && disengaged) { 736 if (apm_engage_disengage_pm(1)) { 737 printf("Warning: APM engage function failed [%s]\n", apm_error()); 738 } 739 } 740 741 apm_suspend_hook_init(apm_default_suspend, "default suspend", APM_MAX_ORDER); 742 apm_resume_hook_init (apm_default_resume , "default resume" , APM_MIN_ORDER); 743 apm_initialized = 1; 744 745 return 0; 746} 747 748int 749apmopen(dev_t dev, int flag, int fmt, struct proc *p) 750{ 751 if (!apm_initialized) { 752 return ENXIO; 753 } 754 switch (minor(dev)) { 755 case 0: /* apm0 */ 756 break; 757 defaults: 758 return (ENXIO); 759 } 760 return 0; 761} 762 763int 764apmclose(dev_t dev, int flag, int fmt, struct proc *p) 765{ 766 return 0; 767} 768 769int 770apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) 771{ 772 int error = 0; 773 int pl; 774 775#if 0 776 printf("APM ioctl: minor = %d, cmd = 0x%x\n", minor(dev), cmd); 777#endif 778 779 pl = splhigh(); 780 if (minor(dev) != 0) { 781 return ENXIO; 782 } 783 if (!apm_initialized) { 784 return ENXIO; 785 } 786 switch (cmd) { 787 case APMIO_SUSPEND: 788 apm_suspend_resume(); 789 break; 790 case APMIO_GETINFO: 791 if (apm_get_info((apm_info_t)addr)) { 792 error = ENXIO; 793 } 794 break; 795 case APMIO_DEFEQV: 796 if (apm_def_eqv((apm_eqv_event_t)addr)) { 797 error = ENOSPC; 798 } 799 break; 800 case APMIO_FLUSHEQV: 801 apm_flush_eqv(); 802 break; 803 case APMIO_ENABLE: 804 apm_event_enable(); 805 break; 806 case APMIO_DISABLE: 807 apm_event_disable(); 808 break; 809 case APMIO_HALTCPU: 810 apm_halt_cpu(); 811 break; 812 case APMIO_NOTHALTCPU: 813 apm_not_halt_cpu(); 814 break; 815 default: 816 error = EINVAL; 817 break; 818 } 819 splx(pl); 820 return error; 821} 822 823#endif /* NAPM > 0 */ 824