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