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