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