apm.c revision 26729
1/* 2 * APM (Advanced Power Management) BIOS Device Driver 3 * 4 * Copyright (c) 1994 UKAI, Fumitoshi. 5 * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org> 6 * Copyright (c) 1996 Nate Williams <nate@FreeBSD.org> 7 * Copyright (c) 1997 Poul-Henning Kamp <phk@FreeBSD.org> 8 * 9 * This software may be used, modified, copied, and distributed, in 10 * both source and binary form provided that the above copyright and 11 * these terms are retained. Under no circumstances is the author 12 * responsible for the proper functioning of this software, nor does 13 * the author assume any responsibility for damages incurred with its 14 * use. 15 * 16 * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) 17 * 18 * $Id: apm.c,v 1.58 1997/06/15 02:19:40 wollman Exp $ 19 */ 20 21#include <sys/param.h> 22#include <sys/conf.h> 23#include <sys/kernel.h> 24#ifdef DEVFS 25#include <sys/devfsext.h> 26#endif /*DEVFS*/ 27#include <sys/systm.h> 28#include <sys/time.h> 29#include <i386/isa/isa_device.h> 30#include <machine/apm_bios.h> 31#include <machine/segments.h> 32#include <machine/clock.h> 33#include <vm/vm.h> 34#include <vm/vm_param.h> 35#include <vm/pmap.h> 36#include <sys/syslog.h> 37#include <i386/apm/apm_setup.h> 38 39static int apm_display __P((int newstate)); 40static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx)); 41static void apm_resume __P((void)); 42 43/* static data */ 44struct apm_softc { 45 int initialized, active; 46 int always_halt_cpu, slow_idle_cpu; 47 int disabled, disengaged; 48 u_int minorversion, majorversion; 49 u_int cs32_base, cs16_base, ds_base; 50 u_int cs_limit, ds_limit; 51 u_int cs_entry; 52 u_int intversion; 53 struct apmhook sc_suspend; 54 struct apmhook sc_resume; 55#ifdef DEVFS 56 void *sc_devfs_token; 57#endif 58}; 59 60static struct apm_softc apm_softc; 61static struct apmhook *hook[NAPM_HOOK]; /* XXX */ 62 63#define is_enabled(foo) ((foo) ? "enabled" : "disabled") 64 65/* Map version number to integer (keeps ordering of version numbers) */ 66#define INTVERSION(major, minor) ((major)*100 + (minor)) 67 68static timeout_t apm_timeout; 69static d_open_t apmopen; 70static d_close_t apmclose; 71static d_ioctl_t apmioctl; 72 73#define CDEV_MAJOR 39 74static struct cdevsw apm_cdevsw = 75 { apmopen, apmclose, noread, nowrite, /*39*/ 76 apmioctl, nostop, nullreset, nodevtotty,/* APM */ 77 seltrue, nommap, NULL , "apm" ,NULL, -1}; 78 79/* setup APM GDT discriptors */ 80static void 81setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code_limit, u_int data_limit) 82{ 83 /* setup 32bit code segment */ 84 gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base; 85 gdt_segs[GAPMCODE32_SEL].ssd_limit = code_limit; 86 87 /* setup 16bit code segment */ 88 gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base; 89 gdt_segs[GAPMCODE16_SEL].ssd_limit = code_limit; 90 91 /* setup data segment */ 92 gdt_segs[GAPMDATA_SEL ].ssd_base = data_base; 93 gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit; 94 95 /* reflect these changes on physical GDT */ 96 ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd); 97 ssdtosd(gdt_segs + GAPMCODE16_SEL, &gdt[GAPMCODE16_SEL].sd); 98 ssdtosd(gdt_segs + GAPMDATA_SEL , &gdt[GAPMDATA_SEL ].sd); 99} 100 101/* 48bit far pointer */ 102struct addr48 { 103 u_long offset; 104 u_short segment; 105} apm_addr; 106 107static int apm_errno; 108 109int 110apm_int(u_long *eax, u_long *ebx, u_long *ecx) 111{ 112 struct apm_bios_arg apa; 113 int cf; 114 115 apa.eax = *eax; 116 apa.ebx = *ebx; 117 apa.ecx = *ecx; 118 cf = apm_bios_call(&apa); 119 *eax = apa.eax; 120 *ebx = apa.ebx; 121 *ecx = apa.ecx; 122 apm_errno = ((*eax) >> 8) & 0xff; 123 return cf; 124} 125 126 127/* enable/disable power management */ 128static int 129apm_enable_disable_pm(int enable) 130{ 131 struct apm_softc *sc = &apm_softc; 132 133 u_long eax, ebx, ecx; 134 135 eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM; 136 137 if (sc->intversion >= INTVERSION(1, 1)) 138 ebx = PMDV_ALLDEV; 139 else 140 ebx = 0xffff; /* APM version 1.0 only */ 141 ecx = enable; 142 return apm_int(&eax, &ebx, &ecx); 143} 144 145static void 146apm_driver_version(int version) 147{ 148 u_long eax, ebx, ecx; 149 150 /* First try APM 1.2 */ 151 eax = (APM_BIOS << 8) | APM_DRVVERSION; 152 ebx = 0x0; 153 ecx = version; 154 if(!apm_int(&eax, &ebx, &ecx)) 155 apm_version = eax & 0xffff; 156} 157 158/* engage/disengage power management (APM 1.1 or later) */ 159static int 160apm_engage_disengage_pm(int engage) 161{ 162 u_long eax, ebx, ecx; 163 164 eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; 165 ebx = PMDV_ALLDEV; 166 ecx = engage; 167 return(apm_int(&eax, &ebx, &ecx)); 168} 169 170/* get PM event */ 171static u_int 172apm_getevent(void) 173{ 174 u_long eax, ebx, ecx; 175 176 eax = (APM_BIOS << 8) | APM_GETPMEVENT; 177 178 ebx = 0; 179 ecx = 0; 180 if (apm_int(&eax, &ebx, &ecx)) 181 return PMEV_NOEVENT; 182 183 return ebx & 0xffff; 184} 185 186/* suspend entire system */ 187static int 188apm_suspend_system(void) 189{ 190 u_long eax, ebx, ecx; 191 192 eax = (APM_BIOS << 8) | APM_SETPWSTATE; 193 ebx = PMDV_ALLDEV; 194 ecx = PMST_SUSPEND; 195 196 if (apm_int(&eax, &ebx, &ecx)) { 197 printf("Entire system suspend failure: errcode = %ld\n", 198 0xff & (eax >> 8)); 199 return 1; 200 } 201 return 0; 202} 203 204/* Display control */ 205/* 206 * Experimental implementation: My laptop machine can't handle this function 207 * If your laptop can control the display via APM, please inform me. 208 * HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org> 209 */ 210static int 211apm_display(int newstate) 212{ 213 u_long eax, ebx, ecx; 214 215 eax = (APM_BIOS << 8) | APM_SETPWSTATE; 216 ebx = PMDV_DISP0; 217 ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND; 218 if (apm_int(&eax, &ebx, &ecx)) { 219 printf("Display off failure: errcode = %ld\n", 220 0xff & (eax >> 8)); 221 return 1; 222 } 223 return 0; 224} 225 226/* 227 * Turn off the entire system. 228 */ 229void 230apm_power_off(void) 231{ 232 u_long eax, ebx, ecx; 233 234 if (!apm_softc.active) 235 return; 236 eax = (APM_BIOS << 8) | APM_SETPWSTATE; 237 ebx = PMDV_ALLDEV; 238 ecx = PMST_OFF; 239 apm_int(&eax, &ebx, &ecx); 240} 241 242/* APM Battery low handler */ 243static void 244apm_battery_low(void) 245{ 246 printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); 247} 248 249/* APM hook manager */ 250static struct apmhook * 251apm_add_hook(struct apmhook **list, struct apmhook *ah) 252{ 253 int s; 254 struct apmhook *p, *prev; 255 256#ifdef APM_DEBUG 257 printf("Add hook \"%s\"\n", ah->ah_name); 258#endif 259 260 s = splhigh(); 261 if (ah == NULL) 262 panic("illegal apm_hook!"); 263 prev = NULL; 264 for (p = *list; p != NULL; prev = p, p = p->ah_next) 265 if (p->ah_order > ah->ah_order) 266 break; 267 268 if (prev == NULL) { 269 ah->ah_next = *list; 270 *list = ah; 271 } else { 272 ah->ah_next = prev->ah_next; 273 prev->ah_next = ah; 274 } 275 splx(s); 276 return ah; 277} 278 279static void 280apm_del_hook(struct apmhook **list, struct apmhook *ah) 281{ 282 int s; 283 struct apmhook *p, *prev; 284 285 s = splhigh(); 286 prev = NULL; 287 for (p = *list; p != NULL; prev = p, p = p->ah_next) 288 if (p == ah) 289 goto deleteit; 290 panic("Tried to delete unregistered apm_hook."); 291 goto nosuchnode; 292deleteit: 293 if (prev != NULL) 294 prev->ah_next = p->ah_next; 295 else 296 *list = p->ah_next; 297nosuchnode: 298 splx(s); 299} 300 301 302/* APM driver calls some functions automatically */ 303static void 304apm_execute_hook(struct apmhook *list) 305{ 306 struct apmhook *p; 307 308 for (p = list; p != NULL; p = p->ah_next) { 309#ifdef APM_DEBUG 310 printf("Execute APM hook \"%s.\"\n", p->ah_name); 311#endif 312 if ((*(p->ah_fun))(p->ah_arg)) 313 printf("Warning: APM hook \"%s\" failed", p->ah_name); 314 } 315} 316 317 318/* establish an apm hook */ 319struct apmhook * 320apm_hook_establish(int apmh, struct apmhook *ah) 321{ 322 if (apmh < 0 || apmh >= NAPM_HOOK) 323 return NULL; 324 325 return apm_add_hook(&hook[apmh], ah); 326} 327 328/* disestablish an apm hook */ 329void 330apm_hook_disestablish(int apmh, struct apmhook *ah) 331{ 332 if (apmh < 0 || apmh >= NAPM_HOOK) 333 return; 334 335 apm_del_hook(&hook[apmh], ah); 336} 337 338 339static struct timeval suspend_time; 340static struct timeval diff_time; 341 342static int 343apm_default_resume(void *arg) 344{ 345 int pl; 346 u_int second, minute, hour; 347 struct timeval resume_time, tmp_time; 348 349 /* modified for adjkerntz */ 350 pl = splsoftclock(); 351 inittodr(0); /* adjust time to RTC */ 352 microtime(&resume_time); 353 tmp_time = time; /* because 'time' is volatile */ 354 timevaladd(&tmp_time, &diff_time); 355 time = tmp_time; 356 splx(pl); 357 second = resume_time.tv_sec - suspend_time.tv_sec; 358 hour = second / 3600; 359 second %= 3600; 360 minute = second / 60; 361 second %= 60; 362 log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", 363 hour, minute, second); 364 return 0; 365} 366 367static int 368apm_default_suspend(void *arg) 369{ 370 int pl; 371 372 pl = splsoftclock(); 373 microtime(&diff_time); 374 inittodr(0); 375 microtime(&suspend_time); 376 timevalsub(&diff_time, &suspend_time); 377 splx(pl); 378 return 0; 379} 380 381static void apm_processevent(void); 382 383/* 384 * Public interface to the suspend/resume: 385 * 386 * Execute suspend and resume hook before and after sleep, respectively. 387 * 388 */ 389 390void 391apm_suspend(void) 392{ 393 struct apm_softc *sc = &apm_softc; 394 395 if (!sc) 396 return; 397 398 if (sc->initialized) { 399 apm_execute_hook(hook[APM_HOOK_SUSPEND]); 400 apm_suspend_system(); 401 apm_processevent(); 402 } 403} 404 405void 406apm_resume(void) 407{ 408 struct apm_softc *sc = &apm_softc; 409 410 if (!sc) 411 return; 412 413 if (sc->initialized) 414 apm_execute_hook(hook[APM_HOOK_RESUME]); 415} 416 417 418/* get APM information */ 419static int 420apm_get_info(apm_info_t aip) 421{ 422 struct apm_softc *sc = &apm_softc; 423 u_long eax, ebx, ecx; 424 425 eax = (APM_BIOS << 8) | APM_GETPWSTATUS; 426 ebx = PMDV_ALLDEV; 427 ecx = 0; 428 429 if (apm_int(&eax, &ebx, &ecx)) 430 return 1; 431 432 aip->ai_acline = (ebx >> 8) & 0xff; 433 aip->ai_batt_stat = ebx & 0xff; 434 aip->ai_batt_life = ecx & 0xff; 435 aip->ai_major = (u_int)sc->majorversion; 436 aip->ai_minor = (u_int)sc->minorversion; 437 aip->ai_status = (u_int)sc->active; 438 439 return 0; 440} 441 442 443/* inform APM BIOS that CPU is idle */ 444void 445apm_cpu_idle(void) 446{ 447 struct apm_softc *sc = &apm_softc; 448 449 if (sc->active) { 450 u_long eax, ebx, ecx; 451 452 eax = (APM_BIOS <<8) | APM_CPUIDLE; 453 ecx = ebx = 0; 454 apm_int(&eax, &ebx, &ecx); 455 } 456 /* 457 * Some APM implementation halts CPU in BIOS, whenever 458 * "CPU-idle" function are invoked, but swtch() of 459 * FreeBSD halts CPU, therefore, CPU is halted twice 460 * in the sched loop. It makes the interrupt latency 461 * terribly long and be able to cause a serious problem 462 * in interrupt processing. We prevent it by removing 463 * "hlt" operation from swtch() and managed it under 464 * APM driver. 465 */ 466 if (!sc->active || sc->always_halt_cpu) 467 __asm("hlt"); /* wait for interrupt */ 468} 469 470/* inform APM BIOS that CPU is busy */ 471void 472apm_cpu_busy(void) 473{ 474 struct apm_softc *sc = &apm_softc; 475 476 /* 477 * The APM specification says this is only necessary if your BIOS 478 * slows down the processor in the idle task, otherwise it's not 479 * necessary. 480 */ 481 if (sc->slow_idle_cpu && sc->active) { 482 u_long eax, ebx, ecx; 483 484 eax = (APM_BIOS <<8) | APM_CPUBUSY; 485 ecx = ebx = 0; 486 apm_int(&eax, &ebx, &ecx); 487 } 488} 489 490 491/* 492 * APM timeout routine: 493 * 494 * This routine is automatically called by timer once per second. 495 */ 496 497static void 498apm_timeout(void *dummy) 499{ 500 struct apm_softc *sc = &apm_softc; 501 502 apm_processevent(); 503 if (sc->active == 1) 504 timeout(apm_timeout, NULL, hz - 1 ); /* More than 1 Hz */ 505} 506 507/* enable APM BIOS */ 508static void 509apm_event_enable(void) 510{ 511 struct apm_softc *sc = &apm_softc; 512 513#ifdef APM_DEBUG 514 printf("called apm_event_enable()\n"); 515#endif 516 if (sc->initialized) { 517 sc->active = 1; 518 apm_timeout(sc); 519 } 520} 521 522/* disable APM BIOS */ 523static void 524apm_event_disable(void) 525{ 526 struct apm_softc *sc = &apm_softc; 527 528#ifdef APM_DEBUG 529 printf("called apm_event_disable()\n"); 530#endif 531 if (sc->initialized) { 532 untimeout(apm_timeout, NULL); 533 sc->active = 0; 534 } 535} 536 537/* halt CPU in scheduling loop */ 538static void 539apm_halt_cpu(void) 540{ 541 struct apm_softc *sc = &apm_softc; 542 543 if (sc->initialized) 544 sc->always_halt_cpu = 1; 545} 546 547/* don't halt CPU in scheduling loop */ 548static void 549apm_not_halt_cpu(void) 550{ 551 struct apm_softc *sc = &apm_softc; 552 553 if (sc->initialized) 554 sc->always_halt_cpu = 0; 555} 556 557/* device driver definitions */ 558static int apmprobe (struct isa_device *); 559static int apmattach(struct isa_device *); 560struct isa_driver apmdriver = { apmprobe, apmattach, "apm" }; 561 562/* 563 * probe APM (dummy): 564 * 565 * APM probing routine is placed on locore.s and apm_init.S because 566 * this process forces the CPU to turn to real mode or V86 mode. 567 * Current version uses real mode, but in a future version, we want 568 * to use V86 mode in APM initialization. 569 */ 570 571static int 572apmprobe(struct isa_device *dvp) 573{ 574 bzero(&apm_softc, sizeof(apm_softc)); 575 576 if ( dvp->id_unit > 0 ) { 577 printf("apm: Only one APM driver supported.\n"); 578 return 0; 579 } 580 switch (apm_version) { 581 case APMINI_CANTFIND: 582 /* silent */ 583 return 0; 584 case APMINI_NOT32BIT: 585 printf("apm: 32bit connection is not supported.\n"); 586 return 0; 587 case APMINI_CONNECTERR: 588 printf("apm: 32-bit connection error.\n"); 589 return 0; 590 } 591 if (dvp->id_flags & 0x20) 592 statclock_disable = 1; 593 return -1; 594} 595 596 597/* Process APM event */ 598static void 599apm_processevent(void) 600{ 601 int apm_event; 602 603#ifdef APM_DEBUG 604# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \ 605 printf("Received APM Event: " #symbol "\n"); 606#else 607# define OPMEV_DEBUGMESSAGE(symbol) case symbol: 608#endif 609 do { 610 apm_event = apm_getevent(); 611 switch (apm_event) { 612 OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 613 apm_suspend(); 614 break; 615 OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 616 apm_suspend(); 617 break; 618 OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 619 apm_suspend(); 620 break; 621 OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 622 apm_suspend(); 623 break; 624 OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 625 apm_resume(); 626 break; 627 OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 628 apm_resume(); 629 break; 630 OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 631 apm_resume(); 632 break; 633 OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 634 apm_battery_low(); 635 apm_suspend(); 636 break; 637 OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 638 break; 639 OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 640 inittodr(0); /* adjust time to RTC */ 641 break; 642 case PMEV_NOEVENT: 643 break; 644 default: 645 printf("Unknown Original APM Event 0x%x\n", apm_event); 646 break; 647 } 648 } while (apm_event != PMEV_NOEVENT); 649} 650 651/* 652 * Attach APM: 653 * 654 * Initialize APM driver (APM BIOS itself has been initialized in locore.s) 655 */ 656 657static int 658apmattach(struct isa_device *dvp) 659{ 660#define APM_KERNBASE KERNBASE 661 struct apm_softc *sc = &apm_softc; 662 663 sc->initialized = 0; 664 665 /* Must be externally enabled */ 666 sc->active = 0; 667 668 /* setup APM parameters */ 669 sc->cs16_base = (apm_cs16_base << 4) + APM_KERNBASE; 670 sc->cs32_base = (apm_cs32_base << 4) + APM_KERNBASE; 671 sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE; 672 sc->cs_limit = apm_cs_limit; 673 sc->ds_limit = apm_ds_limit; 674 sc->cs_entry = apm_cs_entry; 675 676 /* Always call HLT in idle loop */ 677 sc->always_halt_cpu = 1; 678 679 sc->slow_idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0); 680 sc->disabled = ((apm_flags & APM_DISABLED) != 0); 681 sc->disengaged = ((apm_flags & APM_DISENGAGED) != 0); 682 683 /* print bootstrap messages */ 684#ifdef APM_DEBUG 685 printf("apm: APM BIOS version %04x\n", apm_version); 686 printf("apm: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n", 687 sc->cs32_base, sc->cs16_base, sc->ds_base); 688 printf("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n", 689 sc->cs_entry, is_enabled(sc->slow_idle_cpu), 690 is_enabled(!sc->disabled)); 691 printf("apm: CS_limit=0x%x, DS_limit=0x%x\n", 692 sc->cs_limit, sc->ds_limit); 693#endif /* APM_DEBUG */ 694 695#if 0 696 /* Workaround for some buggy APM BIOS implementations */ 697 sc->cs_limit = 0xffff; 698 sc->ds_limit = 0xffff; 699#endif 700 701 /* setup GDT */ 702 setup_apm_gdt(sc->cs32_base, sc->cs16_base, sc->ds_base, 703 sc->cs_limit, sc->ds_limit); 704 705 /* setup entry point 48bit pointer */ 706 apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); 707 apm_addr.offset = sc->cs_entry; 708 709 if ((dvp->id_flags & 0x10)) { 710 if ((dvp->id_flags & 0xf) >= 0x2) { 711 apm_driver_version(0x102); 712 } 713 if (!apm_version && (dvp->id_flags & 0xf) >= 0x1) { 714 apm_driver_version(0x101); 715 } 716 } else { 717 apm_driver_version(0x102); 718 if (!apm_version) 719 apm_driver_version(0x101); 720 } 721 if (!apm_version) 722 apm_version = 0x100; 723 724 sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + 725 ((apm_version & 0x000f) >> 0); 726 sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + 727 ((apm_version & 0x0f00) >> 8); 728 729 sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); 730 731#ifdef APM_DEBUG 732 if (sc->intversion >= INTVERSION(1, 1)) 733 printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged)); 734#endif 735 736 printf("apm: found APM BIOS version %d.%d\n", 737 sc->majorversion, sc->minorversion); 738 739#ifdef APM_DEBUG 740 printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu)); 741#endif 742 743 /* enable power management */ 744 if (sc->disabled) { 745 if (apm_enable_disable_pm(1)) { 746#ifdef APM_DEBUG 747 printf("apm: *Warning* enable function failed! [%x]\n", 748 apm_errno); 749#endif 750 } 751 } 752 753 /* engage power managment (APM 1.1 or later) */ 754 if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) { 755 if (apm_engage_disengage_pm(1)) { 756#ifdef APM_DEBUG 757 printf("apm: *Warning* engage function failed err=[%x]", 758 apm_errno); 759 printf(" (Docked or using external power?).\n"); 760#endif 761 } 762 } 763 764 /* default suspend hook */ 765 sc->sc_suspend.ah_fun = apm_default_suspend; 766 sc->sc_suspend.ah_arg = sc; 767 sc->sc_suspend.ah_name = "default suspend"; 768 sc->sc_suspend.ah_order = APM_MAX_ORDER; 769 770 /* default resume hook */ 771 sc->sc_resume.ah_fun = apm_default_resume; 772 sc->sc_resume.ah_arg = sc; 773 sc->sc_resume.ah_name = "default resume"; 774 sc->sc_resume.ah_order = APM_MIN_ORDER; 775 776 apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); 777 apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume); 778 779 apm_event_enable(); 780 781 sc->initialized = 1; 782 783#ifdef DEVFS 784 sc->sc_devfs_token = 785 devfs_add_devswf(&apm_cdevsw, 0, DV_CHR, 0, 0, 0600, "apm"); 786#endif 787 return 0; 788} 789 790static int 791apmopen(dev_t dev, int flag, int fmt, struct proc *p) 792{ 793 struct apm_softc *sc = &apm_softc; 794 795 if (minor(dev) != 0 || !sc->initialized) 796 return (ENXIO); 797 798 return 0; 799} 800 801static int 802apmclose(dev_t dev, int flag, int fmt, struct proc *p) 803{ 804 return 0; 805} 806 807static int 808apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) 809{ 810 struct apm_softc *sc = &apm_softc; 811 int error = 0; 812 int newstate; 813 814 if (minor(dev) != 0 || !sc->initialized) 815 return (ENXIO); 816#ifdef APM_DEBUG 817 printf("APM ioctl: cmd = 0x%x\n", cmd); 818#endif 819 switch (cmd) { 820 case APMIO_SUSPEND: 821 if ( sc->active) 822 apm_suspend(); 823 else 824 error = EINVAL; 825 break; 826 case APMIO_GETINFO: 827 if (apm_get_info((apm_info_t)addr)) 828 error = ENXIO; 829 break; 830 case APMIO_ENABLE: 831 apm_event_enable(); 832 break; 833 case APMIO_DISABLE: 834 apm_event_disable(); 835 break; 836 case APMIO_HALTCPU: 837 apm_halt_cpu(); 838 break; 839 case APMIO_NOTHALTCPU: 840 apm_not_halt_cpu(); 841 break; 842 case APMIO_DISPLAY: 843 newstate = *(int *)addr; 844 if (apm_display(newstate)) 845 error = ENXIO; 846 break; 847 case APMIO_BIOS: 848 if (apm_bios_call((struct apm_bios_arg*)addr)) 849 error = EIO; 850 break; 851 default: 852 error = EINVAL; 853 break; 854 } 855 return error; 856} 857 858 859static apm_devsw_installed = 0; 860 861static void 862apm_drvinit(void *unused) 863{ 864 dev_t dev; 865 866 if( ! apm_devsw_installed ) { 867 dev = makedev(CDEV_MAJOR,0); 868 cdevsw_add(&dev,&apm_cdevsw,NULL); 869 apm_devsw_installed = 1; 870 } 871} 872 873SYSINIT(apmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,apm_drvinit,NULL) 874