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