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