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