apm.c revision 74810
165556Sjasone/* 265556Sjasone * APM (Advanced Power Management) BIOS Device Driver 365556Sjasone * 465556Sjasone * Copyright (c) 1994 UKAI, Fumitoshi. 565556Sjasone * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org> 665556Sjasone * Copyright (c) 1996 Nate Williams <nate@FreeBSD.org> 765556Sjasone * Copyright (c) 1997 Poul-Henning Kamp <phk@FreeBSD.org> 865556Sjasone * 965556Sjasone * This software may be used, modified, copied, and distributed, in 1065556Sjasone * both source and binary form provided that the above copyright and 1165556Sjasone * these terms are retained. Under no circumstances is the author 1265556Sjasone * responsible for the proper functioning of this software, nor does 1365556Sjasone * the author assume any responsibility for damages incurred with its 1465556Sjasone * use. 1565556Sjasone * 1665556Sjasone * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) 1769012Sjhb * 1865556Sjasone * $FreeBSD: head/sys/i386/bios/apm.c 74810 2001-03-26 12:41:29Z phk $ 1965556Sjasone */ 2069012Sjhb 2165556Sjasone#include <sys/param.h> 2265556Sjasone#include <sys/systm.h> 2365556Sjasone#include <sys/eventhandler.h> 2465556Sjasone#include <sys/conf.h> 2565556Sjasone#include <sys/kernel.h> 2665556Sjasone#include <sys/time.h> 2765556Sjasone#include <sys/reboot.h> 2865556Sjasone#include <sys/bus.h> 2965556Sjasone#include <sys/selinfo.h> 3065556Sjasone#include <sys/poll.h> 3168420Sjhb#include <sys/fcntl.h> 3268420Sjhb#include <sys/uio.h> 3365556Sjasone#include <sys/signalvar.h> 3465556Sjasone#include <sys/sysctl.h> 35116182Sobrien#include <machine/apm_bios.h> 36116182Sobrien#include <machine/segments.h> 37116182Sobrien#include <machine/clock.h> 3870035Sjhb#include <vm/vm.h> 3968420Sjhb#include <vm/vm_param.h> 40103787Sjeff#include <vm/pmap.h> 4168420Sjhb#include <sys/syslog.h> 4270035Sjhb 43103787Sjeff#include <machine/pc/bios.h> 4470035Sjhb#include <machine/vm86.h> 4570705Sjhb 4665556Sjasone#include <i386/apm/apm.h> 4768420Sjhb 4887793Sjhb/* Used by the apm_saver screen saver module */ 4965556Sjasoneint apm_display __P((int newstate)); 5068420Sjhbstruct apm_softc apm_softc; 5178784Sjhb 5265556Sjasonestatic void apm_resume __P((void)); 5393503Sjakestatic int apm_bioscall(void); 5493950Sjakestatic int apm_check_function_supported __P((u_int version, u_int func)); 5593950Sjake 5693950Sjakestatic u_long apm_version; 5793503Sjake 58103787Sjeffint apm_evindex; 5970035Sjhb 6070035Sjhb#define SCFLAG_ONORMAL 0x0000001 6178784Sjhb#define SCFLAG_OCTL 0x0000002 6278784Sjhb#define SCFLAG_OPEN (SCFLAG_ONORMAL|SCFLAG_OCTL) 6378784Sjhb 6478784Sjhb#define APMDEV(dev) (minor(dev)&0x0f) 6568420Sjhb#define APMDEV_NORMAL 0 6668420Sjhb#define APMDEV_CTL 8 6768420Sjhb 6868420Sjhbstatic struct apmhook *hook[NAPM_HOOK]; /* XXX */ 6968420Sjhb 7068420Sjhb#define is_enabled(foo) ((foo) ? "enabled" : "disabled") 7168420Sjhb 7268420Sjhb/* Map version number to integer (keeps ordering of version numbers) */ 7393503Sjake#define INTVERSION(major, minor) ((major)*100 + (minor)) 7493503Sjake 7568420Sjhbstatic struct callout_handle apm_timeout_ch = 7668420Sjhb CALLOUT_HANDLE_INITIALIZER(&apm_timeout_ch); 7793503Sjake 7893503Sjakestatic timeout_t apm_timeout; 7970705Sjhbstatic d_open_t apmopen; 8070705Sjhbstatic d_close_t apmclose; 8170705Sjhbstatic d_write_t apmwrite; 8270705Sjhbstatic d_ioctl_t apmioctl; 8377843Speterstatic d_poll_t apmpoll; 8477900Speter 8570705Sjhb#define CDEV_MAJOR 39 8665556Sjasonestatic struct cdevsw apm_cdevsw = { 8777843Speter /* open */ apmopen, 8877900Speter /* close */ apmclose, 8970705Sjhb /* read */ noread, 9065556Sjasone /* write */ apmwrite, 9165556Sjasone /* ioctl */ apmioctl, 9270705Sjhb /* poll */ apmpoll, 9365556Sjasone /* mmap */ nommap, 9493503Sjake /* strategy */ nostrategy, 9593503Sjake /* name */ "apm", 9693503Sjake /* maj */ CDEV_MAJOR, 9765556Sjasone /* dump */ nodump, 9865556Sjasone /* psize */ nopsize, 9968420Sjhb /* flags */ 0, 10093503Sjake}; 10193503Sjake 10277900Speterstatic int apm_suspend_delay = 1; 10370705Sjhbstatic int apm_standby_delay = 1; 10493503Sjakestatic int apm_debug = 0; 10568420Sjhb 106103787Sjeff#define APM_DPRINT(args...) do { \ 107103787Sjeff if (apm_debug) { \ 108103787Sjeff printf(args); \ 109103787Sjeff } \ 110103787Sjeff} while (0) 111103787Sjeff 112103787SjeffSYSCTL_INT(_machdep, OID_AUTO, apm_suspend_delay, CTLFLAG_RW, &apm_suspend_delay, 1, ""); 113103787SjeffSYSCTL_INT(_machdep, OID_AUTO, apm_standby_delay, CTLFLAG_RW, &apm_standby_delay, 1, ""); 114103787SjeffSYSCTL_INT(_debug, OID_AUTO, apm_debug, CTLFLAG_RW, &apm_debug, 0, ""); 115103787Sjeff 116103787Sjeff/* 117103787Sjeff * return 0 if the function successfull, 118103787Sjeff * return 1 if the function unsuccessfull, 119103787Sjeff * return -1 if the function unsupported. 120103787Sjeff */ 121103787Sjeffstatic int 122103787Sjeffapm_bioscall(void) 123103787Sjeff{ 124103787Sjeff struct apm_softc *sc = &apm_softc; 125103787Sjeff int errno = 0; 126103787Sjeff u_int apm_func = sc->bios.r.eax & 0xff; 127103787Sjeff 128103787Sjeff if (!apm_check_function_supported(sc->intversion, apm_func)) { 129103787Sjeff APM_DPRINT("apm_bioscall: function 0x%x is not supported in v%d.%d\n", 130103787Sjeff apm_func, sc->majorversion, sc->minorversion); 131103787Sjeff return (-1); 132103787Sjeff } 133103787Sjeff 134103787Sjeff sc->bios_busy = 1; 135103787Sjeff if (sc->connectmode == APM_PROT32CONNECT) { 136103787Sjeff set_bios_selectors(&sc->bios.seg, 137103787Sjeff BIOSCODE_FLAG | BIOSDATA_FLAG); 138103787Sjeff errno = bios32(&sc->bios.r, 139103787Sjeff sc->bios.entry, GSEL(GBIOSCODE32_SEL, SEL_KPL)); 140103787Sjeff } else { 141103787Sjeff errno = bios16(&sc->bios, NULL); 142103787Sjeff } 143103787Sjeff sc->bios_busy = 0; 144116697Srwatson return (errno); 145116697Srwatson} 146116697Srwatson 147103787Sjeff/* check whether APM function is supported (1) or not (0). */ 148103787Sjeffstatic int 149103787Sjeffapm_check_function_supported(u_int version, u_int func) 150103787Sjeff{ 151103787Sjeff /* except driver version */ 152103787Sjeff if (func == APM_DRVVERSION) { 153103787Sjeff return (1); 154103787Sjeff } 155103787Sjeff 156103787Sjeff switch (version) { 157103787Sjeff case INTVERSION(1, 0): 158103787Sjeff if (func > APM_GETPMEVENT) { 159103787Sjeff return (0); /* not supported */ 160103787Sjeff } 161103787Sjeff break; 162103787Sjeff case INTVERSION(1, 1): 163103787Sjeff if (func > APM_ENGAGEDISENGAGEPM && 164103787Sjeff func < APM_OEMFUNC) { 165103787Sjeff return (0); /* not supported */ 166103787Sjeff } 167103787Sjeff break; 16868420Sjhb case INTVERSION(1, 2): 16993503Sjake break; 17093503Sjake } 17193503Sjake 17268420Sjhb return (1); /* supported */ 17368420Sjhb} 174103787Sjeff 175103787Sjeff/* enable/disable power management */ 176103787Sjeffstatic int 17774903Sjhbapm_enable_disable_pm(int enable) 178103787Sjeff{ 179103787Sjeff struct apm_softc *sc = &apm_softc; 18087793Sjhb 18193503Sjake sc->bios.r.eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM; 18291904Sjhb 18368420Sjhb if (sc->intversion >= INTVERSION(1, 1)) 18469880Sjhb sc->bios.r.ebx = PMDV_ALLDEV; 18569880Sjhb else 18668420Sjhb sc->bios.r.ebx = 0xffff; /* APM version 1.0 only */ 18768420Sjhb sc->bios.r.ecx = enable; 18893503Sjake sc->bios.r.edx = 0; 18993503Sjake return (apm_bioscall()); 19093503Sjake} 191103787Sjeff 19287793Sjhb/* register driver version (APM 1.1 or later) */ 193116101Sjhbstatic int 19487793Sjhbapm_driver_version(int version) 195116101Sjhb{ 19693503Sjake struct apm_softc *sc = &apm_softc; 197103787Sjeff 198103787Sjeff sc->bios.r.eax = (APM_BIOS << 8) | APM_DRVVERSION; 199103787Sjeff sc->bios.r.ebx = 0x0; 200114471Sjulian sc->bios.r.ecx = version; 201103995Sjeff sc->bios.r.edx = 0; 202103787Sjeff 203103787Sjeff if (apm_bioscall() == 0 && sc->bios.r.eax == version) 204103787Sjeff return (0); 205103787Sjeff 206103787Sjeff /* Some old BIOSes don't return the connection version in %ax. */ 207103787Sjeff if (sc->bios.r.eax == ((APM_BIOS << 8) | APM_DRVVERSION)) 208103787Sjeff return (0); 209103787Sjeff 210103787Sjeff return (1); 211103787Sjeff} 212103787Sjeff 21368420Sjhb/* engage/disengage power management (APM 1.1 or later) */ 21468420Sjhbstatic int 21568420Sjhbapm_engage_disengage_pm(int engage) 21668420Sjhb{ 21768420Sjhb struct apm_softc *sc = &apm_softc; 218103787Sjeff 21993503Sjake sc->bios.r.eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM; 22091904Sjhb sc->bios.r.ebx = PMDV_ALLDEV; 221112105Sjhb sc->bios.r.ecx = engage; 222112105Sjhb sc->bios.r.edx = 0; 223112105Sjhb return (apm_bioscall()); 22493503Sjake} 22568420Sjhb 22693503Sjake/* get PM event */ 22768420Sjhbstatic u_int 22868782Sjhbapm_getevent(void) 22993503Sjake{ 23068782Sjhb struct apm_softc *sc = &apm_softc; 23193503Sjake 23293503Sjake sc->bios.r.eax = (APM_BIOS << 8) | APM_GETPMEVENT; 23393503Sjake 23493503Sjake sc->bios.r.ebx = 0; 23593503Sjake sc->bios.r.ecx = 0; 23668420Sjhb sc->bios.r.edx = 0; 23768420Sjhb if (apm_bioscall()) 23893503Sjake return (PMEV_NOEVENT); 23968420Sjhb return (sc->bios.r.ebx & 0xffff); 24093503Sjake} 24193503Sjake 24293503Sjake/* suspend entire system */ 24393503Sjakestatic int 24493503Sjakeapm_suspend_system(int state) 24593503Sjake{ 246103787Sjeff struct apm_softc *sc = &apm_softc; 247103787Sjeff 248103787Sjeff sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; 249103787Sjeff sc->bios.r.ebx = PMDV_ALLDEV; 250103787Sjeff sc->bios.r.ecx = state; 251103787Sjeff sc->bios.r.edx = 0; 252116101Sjhb 25368420Sjhb if (apm_bioscall()) { 25468420Sjhb printf("Entire system suspend failure: errcode = %d\n", 25570035Sjhb 0xff & (sc->bios.r.eax >> 8)); 25670035Sjhb return 1; 25770035Sjhb } 25870035Sjhb return 0; 25970035Sjhb} 26070035Sjhb 26170035Sjhb/* Display control */ 26270035Sjhb/* 26370035Sjhb * Experimental implementation: My laptop machine can't handle this function 26470035Sjhb * If your laptop can control the display via APM, please inform me. 26570035Sjhb * HOSOKAWA, Tatsumi <hosokawa@jp.FreeBSD.org> 26672755Sjhb */ 26772755Sjhbint 26872755Sjhbapm_display(int newstate) 26970035Sjhb{ 270118269Sjhb struct apm_softc *sc = &apm_softc; 271118269Sjhb 272118269Sjhb sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; 27372755Sjhb sc->bios.r.ebx = PMDV_DISP0; 27470035Sjhb sc->bios.r.ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND; 27570035Sjhb sc->bios.r.edx = 0; 27670035Sjhb if (apm_bioscall() == 0) { 27770035Sjhb return 0; 27870035Sjhb } 27970035Sjhb 280118269Sjhb /* If failed, then try to blank all display devices instead. */ 281118269Sjhb sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; 282118269Sjhb sc->bios.r.ebx = PMDV_DISPALL; /* all display devices */ 28372755Sjhb sc->bios.r.ecx = newstate ? PMST_APMENABLED:PMST_SUSPEND; 284118269Sjhb sc->bios.r.edx = 0; 285118269Sjhb if (apm_bioscall() == 0) { 286118269Sjhb return 0; 287118269Sjhb } 28872755Sjhb printf("Display off failure: errcode = %d\n", 289118269Sjhb 0xff & (sc->bios.r.eax >> 8)); 29070035Sjhb return 1; 29170035Sjhb} 29270035Sjhb 29370035Sjhb/* 29470035Sjhb * Turn off the entire system. 29570035Sjhb */ 29670035Sjhbstatic void 29770035Sjhbapm_power_off(void *junk, int howto) 29870035Sjhb{ 29970035Sjhb struct apm_softc *sc = &apm_softc; 30070035Sjhb 30170035Sjhb /* Not halting powering off, or not active */ 30270035Sjhb if (!(howto & RB_POWEROFF) || !apm_softc.active) 30370035Sjhb return; 30472755Sjhb sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; 30572755Sjhb sc->bios.r.ebx = PMDV_ALLDEV; 30672755Sjhb sc->bios.r.ecx = PMST_OFF; 30772755Sjhb sc->bios.r.edx = 0; 30872755Sjhb (void) apm_bioscall(); 30970035Sjhb} 31072755Sjhb 31170035Sjhb/* APM Battery low handler */ 31293503Sjakestatic void 31393503Sjakeapm_battery_low(void) 31493503Sjake{ 31593503Sjake printf("\007\007 * * * BATTERY IS LOW * * * \007\007"); 31693503Sjake} 31793503Sjake 31893503Sjake/* APM hook manager */ 31972755Sjhbstatic struct apmhook * 32070035Sjhbapm_add_hook(struct apmhook **list, struct apmhook *ah) 32170035Sjhb{ 32270035Sjhb int s; 32370035Sjhb struct apmhook *p, *prev; 32470035Sjhb 32570035Sjhb APM_DPRINT("Add hook \"%s\"\n", ah->ah_name); 32670035Sjhb 32770035Sjhb s = splhigh(); 32870035Sjhb if (ah == NULL) 32970035Sjhb panic("illegal apm_hook!"); 33070035Sjhb prev = NULL; 331 for (p = *list; p != NULL; prev = p, p = p->ah_next) 332 if (p->ah_order > ah->ah_order) 333 break; 334 335 if (prev == NULL) { 336 ah->ah_next = *list; 337 *list = ah; 338 } else { 339 ah->ah_next = prev->ah_next; 340 prev->ah_next = ah; 341 } 342 splx(s); 343 return ah; 344} 345 346static void 347apm_del_hook(struct apmhook **list, struct apmhook *ah) 348{ 349 int s; 350 struct apmhook *p, *prev; 351 352 s = splhigh(); 353 prev = NULL; 354 for (p = *list; p != NULL; prev = p, p = p->ah_next) 355 if (p == ah) 356 goto deleteit; 357 panic("Tried to delete unregistered apm_hook."); 358 goto nosuchnode; 359deleteit: 360 if (prev != NULL) 361 prev->ah_next = p->ah_next; 362 else 363 *list = p->ah_next; 364nosuchnode: 365 splx(s); 366} 367 368 369/* APM driver calls some functions automatically */ 370static void 371apm_execute_hook(struct apmhook *list) 372{ 373 struct apmhook *p; 374 375 for (p = list; p != NULL; p = p->ah_next) { 376 APM_DPRINT("Execute APM hook \"%s.\"\n", p->ah_name); 377 if ((*(p->ah_fun))(p->ah_arg)) 378 printf("Warning: APM hook \"%s\" failed", p->ah_name); 379 } 380} 381 382 383/* establish an apm hook */ 384struct apmhook * 385apm_hook_establish(int apmh, struct apmhook *ah) 386{ 387 if (apmh < 0 || apmh >= NAPM_HOOK) 388 return NULL; 389 390 return apm_add_hook(&hook[apmh], ah); 391} 392 393/* disestablish an apm hook */ 394void 395apm_hook_disestablish(int apmh, struct apmhook *ah) 396{ 397 if (apmh < 0 || apmh >= NAPM_HOOK) 398 return; 399 400 apm_del_hook(&hook[apmh], ah); 401} 402 403static int apm_record_event __P((struct apm_softc *, u_int)); 404static void apm_processevent(void); 405 406static u_int apm_op_inprog = 0; 407 408static void 409apm_do_suspend(void) 410{ 411 struct apm_softc *sc = &apm_softc; 412 int error; 413 414 if (!sc) 415 return; 416 417 apm_op_inprog = 0; 418 sc->suspends = sc->suspend_countdown = 0; 419 420 if (sc->initialized) { 421 error = DEVICE_SUSPEND(root_bus); 422 if (error) { 423 DEVICE_RESUME(root_bus); 424 } else { 425 apm_execute_hook(hook[APM_HOOK_SUSPEND]); 426 if (apm_suspend_system(PMST_SUSPEND) == 0) { 427 sc->suspending = 1; 428 apm_processevent(); 429 } else { 430 /* Failure, 'resume' the system again */ 431 apm_execute_hook(hook[APM_HOOK_RESUME]); 432 DEVICE_RESUME(root_bus); 433 } 434 } 435 } 436} 437 438static void 439apm_do_standby(void) 440{ 441 struct apm_softc *sc = &apm_softc; 442 443 if (!sc) 444 return; 445 446 apm_op_inprog = 0; 447 sc->standbys = sc->standby_countdown = 0; 448 449 if (sc->initialized) { 450 /* 451 * As far as standby, we don't need to execute 452 * all of suspend hooks. 453 */ 454 if (apm_suspend_system(PMST_STANDBY) == 0) 455 apm_processevent(); 456 } 457} 458 459static void 460apm_lastreq_notify(void) 461{ 462 struct apm_softc *sc = &apm_softc; 463 464 sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; 465 sc->bios.r.ebx = PMDV_ALLDEV; 466 sc->bios.r.ecx = PMST_LASTREQNOTIFY; 467 sc->bios.r.edx = 0; 468 apm_bioscall(); 469} 470 471static int 472apm_lastreq_rejected(void) 473{ 474 struct apm_softc *sc = &apm_softc; 475 476 if (apm_op_inprog == 0) { 477 return 1; /* no operation in progress */ 478 } 479 480 sc->bios.r.eax = (APM_BIOS << 8) | APM_SETPWSTATE; 481 sc->bios.r.ebx = PMDV_ALLDEV; 482 sc->bios.r.ecx = PMST_LASTREQREJECT; 483 sc->bios.r.edx = 0; 484 485 if (apm_bioscall()) { 486 APM_DPRINT("apm_lastreq_rejected: failed\n"); 487 return 1; 488 } 489 apm_op_inprog = 0; 490 return 0; 491} 492 493/* 494 * Public interface to the suspend/resume: 495 * 496 * Execute suspend and resume hook before and after sleep, respectively. 497 * 498 */ 499 500void 501apm_suspend(int state) 502{ 503 struct apm_softc *sc = &apm_softc; 504 505 if (!sc->initialized) 506 return; 507 508 switch (state) { 509 case PMST_SUSPEND: 510 if (sc->suspends) 511 return; 512 sc->suspends++; 513 sc->suspend_countdown = apm_suspend_delay; 514 break; 515 case PMST_STANDBY: 516 if (sc->standbys) 517 return; 518 sc->standbys++; 519 sc->standby_countdown = apm_standby_delay; 520 break; 521 default: 522 printf("apm_suspend: Unknown Suspend state 0x%x\n", state); 523 return; 524 } 525 526 apm_op_inprog++; 527 apm_lastreq_notify(); 528} 529 530void 531apm_resume(void) 532{ 533 struct apm_softc *sc = &apm_softc; 534 535 if (!sc) 536 return; 537 538 if (sc->suspending == 0) 539 return; 540 541 sc->suspending = 0; 542 if (sc->initialized) { 543 apm_execute_hook(hook[APM_HOOK_RESUME]); 544 DEVICE_RESUME(root_bus); 545 } 546} 547 548 549/* get power status per battery */ 550static int 551apm_get_pwstatus(apm_pwstatus_t app) 552{ 553 struct apm_softc *sc = &apm_softc; 554 555 if (app->ap_device != PMDV_ALLDEV && 556 (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL)) 557 return 1; 558 559 sc->bios.r.eax = (APM_BIOS << 8) | APM_GETPWSTATUS; 560 sc->bios.r.ebx = app->ap_device; 561 sc->bios.r.ecx = 0; 562 sc->bios.r.edx = 0xffff; /* default to unknown battery time */ 563 564 if (apm_bioscall()) 565 return 1; 566 567 app->ap_acline = (sc->bios.r.ebx >> 8) & 0xff; 568 app->ap_batt_stat = sc->bios.r.ebx & 0xff; 569 app->ap_batt_flag = (sc->bios.r.ecx >> 8) & 0xff; 570 app->ap_batt_life = sc->bios.r.ecx & 0xff; 571 sc->bios.r.edx &= 0xffff; 572 if (sc->bios.r.edx == 0xffff) /* Time is unknown */ 573 app->ap_batt_time = -1; 574 else if (sc->bios.r.edx & 0x8000) /* Time is in minutes */ 575 app->ap_batt_time = (sc->bios.r.edx & 0x7fff) * 60; 576 else /* Time is in seconds */ 577 app->ap_batt_time = sc->bios.r.edx; 578 579 return 0; 580} 581 582 583/* get APM information */ 584static int 585apm_get_info(apm_info_t aip) 586{ 587 struct apm_softc *sc = &apm_softc; 588 struct apm_pwstatus aps; 589 590 bzero(&aps, sizeof(aps)); 591 aps.ap_device = PMDV_ALLDEV; 592 if (apm_get_pwstatus(&aps)) 593 return 1; 594 595 aip->ai_infoversion = 1; 596 aip->ai_acline = aps.ap_acline; 597 aip->ai_batt_stat = aps.ap_batt_stat; 598 aip->ai_batt_life = aps.ap_batt_life; 599 aip->ai_batt_time = aps.ap_batt_time; 600 aip->ai_major = (u_int)sc->majorversion; 601 aip->ai_minor = (u_int)sc->minorversion; 602 aip->ai_status = (u_int)sc->active; 603 604 sc->bios.r.eax = (APM_BIOS << 8) | APM_GETCAPABILITIES; 605 sc->bios.r.ebx = 0; 606 sc->bios.r.ecx = 0; 607 sc->bios.r.edx = 0; 608 if (apm_bioscall()) { 609 aip->ai_batteries = -1; /* Unknown */ 610 aip->ai_capabilities = 0xff00; /* Unknown, with no bits set */ 611 } else { 612 aip->ai_batteries = sc->bios.r.ebx & 0xff; 613 aip->ai_capabilities = sc->bios.r.ecx & 0xf; 614 } 615 616 bzero(aip->ai_spare, sizeof aip->ai_spare); 617 618 return 0; 619} 620 621 622/* inform APM BIOS that CPU is idle */ 623void 624apm_cpu_idle(void) 625{ 626 struct apm_softc *sc = &apm_softc; 627 628 if (sc->active) { 629 630 sc->bios.r.eax = (APM_BIOS <<8) | APM_CPUIDLE; 631 sc->bios.r.edx = sc->bios.r.ecx = sc->bios.r.ebx = 0; 632 (void) apm_bioscall(); 633 } 634 /* 635 * Some APM implementation halts CPU in BIOS, whenever 636 * "CPU-idle" function are invoked, but swtch() of 637 * FreeBSD halts CPU, therefore, CPU is halted twice 638 * in the sched loop. It makes the interrupt latency 639 * terribly long and be able to cause a serious problem 640 * in interrupt processing. We prevent it by removing 641 * "hlt" operation from swtch() and managed it under 642 * APM driver. 643 */ 644 if (!sc->active || sc->always_halt_cpu) 645 __asm("hlt"); /* wait for interrupt */ 646} 647 648/* inform APM BIOS that CPU is busy */ 649void 650apm_cpu_busy(void) 651{ 652 struct apm_softc *sc = &apm_softc; 653 654 /* 655 * The APM specification says this is only necessary if your BIOS 656 * slows down the processor in the idle task, otherwise it's not 657 * necessary. 658 */ 659 if (sc->slow_idle_cpu && sc->active) { 660 661 sc->bios.r.eax = (APM_BIOS <<8) | APM_CPUBUSY; 662 sc->bios.r.edx = sc->bios.r.ecx = sc->bios.r.ebx = 0; 663 apm_bioscall(); 664 } 665} 666 667 668/* 669 * APM timeout routine: 670 * 671 * This routine is automatically called by timer once per second. 672 */ 673 674static void 675apm_timeout(void *dummy) 676{ 677 struct apm_softc *sc = &apm_softc; 678 679 if (apm_op_inprog) 680 apm_lastreq_notify(); 681 682 if (sc->standbys && sc->standby_countdown-- <= 0) 683 apm_do_standby(); 684 685 if (sc->suspends && sc->suspend_countdown-- <= 0) 686 apm_do_suspend(); 687 688 if (!sc->bios_busy) 689 apm_processevent(); 690 691 if (sc->active == 1) 692 /* Run slightly more oftan than 1 Hz */ 693 apm_timeout_ch = timeout(apm_timeout, NULL, hz - 1); 694} 695 696/* enable APM BIOS */ 697static void 698apm_event_enable(void) 699{ 700 struct apm_softc *sc = &apm_softc; 701 702 APM_DPRINT("called apm_event_enable()\n"); 703 if (sc->initialized) { 704 sc->active = 1; 705 apm_timeout(sc); 706 } 707} 708 709/* disable APM BIOS */ 710static void 711apm_event_disable(void) 712{ 713 struct apm_softc *sc = &apm_softc; 714 715 APM_DPRINT("called apm_event_disable()\n"); 716 if (sc->initialized) { 717 untimeout(apm_timeout, NULL, apm_timeout_ch); 718 sc->active = 0; 719 } 720} 721 722/* halt CPU in scheduling loop */ 723static void 724apm_halt_cpu(void) 725{ 726 struct apm_softc *sc = &apm_softc; 727 728 if (sc->initialized) 729 sc->always_halt_cpu = 1; 730} 731 732/* don't halt CPU in scheduling loop */ 733static void 734apm_not_halt_cpu(void) 735{ 736 struct apm_softc *sc = &apm_softc; 737 738 if (sc->initialized) 739 sc->always_halt_cpu = 0; 740} 741 742/* device driver definitions */ 743 744/* 745 * Create "connection point" 746 */ 747static void 748apm_identify(driver_t *driver, device_t parent) 749{ 750 device_t child; 751 752 child = BUS_ADD_CHILD(parent, 0, "apm", 0); 753 if (child == NULL) 754 panic("apm_identify"); 755} 756 757/* 758 * probe for APM BIOS 759 */ 760static int 761apm_probe(device_t dev) 762{ 763#define APM_KERNBASE KERNBASE 764 struct vm86frame vmf; 765 struct apm_softc *sc = &apm_softc; 766 int disabled, flags; 767 768 device_set_desc(dev, "APM BIOS"); 769 770 if (resource_int_value("apm", 0, "disabled", &disabled) != 0) 771 disabled = 0; 772 if (disabled) 773 return ENXIO; 774 775 if (device_get_unit(dev) > 0) { 776 printf("apm: Only one APM driver supported.\n"); 777 return ENXIO; 778 } 779 780 if (resource_int_value("apm", 0, "flags", &flags) != 0) 781 flags = 0; 782 783 bzero(&vmf, sizeof(struct vm86frame)); /* safety */ 784 bzero(&apm_softc, sizeof(apm_softc)); 785 vmf.vmf_ah = APM_BIOS; 786 vmf.vmf_al = APM_INSTCHECK; 787 vmf.vmf_bx = 0; 788 if (vm86_intcall(APM_INT, &vmf)) 789 return ENXIO; /* APM not found */ 790 if (vmf.vmf_bx != 0x504d) { 791 printf("apm: incorrect signature (0x%x)\n", vmf.vmf_bx); 792 return ENXIO; 793 } 794 if ((vmf.vmf_cx & (APM_32BIT_SUPPORT | APM_16BIT_SUPPORT)) == 0) { 795 printf("apm: protected mode connections are not supported\n"); 796 return ENXIO; 797 } 798 799 apm_version = vmf.vmf_ax; 800 sc->slow_idle_cpu = ((vmf.vmf_cx & APM_CPUIDLE_SLOW) != 0); 801 sc->disabled = ((vmf.vmf_cx & APM_DISABLED) != 0); 802 sc->disengaged = ((vmf.vmf_cx & APM_DISENGAGED) != 0); 803 804 vmf.vmf_ah = APM_BIOS; 805 vmf.vmf_al = APM_DISCONNECT; 806 vmf.vmf_bx = 0; 807 vm86_intcall(APM_INT, &vmf); /* disconnect, just in case */ 808 809 if ((vmf.vmf_cx & APM_32BIT_SUPPORT) != 0) { 810 vmf.vmf_ah = APM_BIOS; 811 vmf.vmf_al = APM_PROT32CONNECT; 812 vmf.vmf_bx = 0; 813 if (vm86_intcall(APM_INT, &vmf)) { 814 printf("apm: 32-bit connection error.\n"); 815 return (ENXIO); 816 } 817 sc->bios.seg.code32.base = (vmf.vmf_ax << 4) + APM_KERNBASE; 818 sc->bios.seg.code32.limit = 0xffff; 819 sc->bios.seg.code16.base = (vmf.vmf_cx << 4) + APM_KERNBASE; 820 sc->bios.seg.code16.limit = 0xffff; 821 sc->bios.seg.data.base = (vmf.vmf_dx << 4) + APM_KERNBASE; 822 sc->bios.seg.data.limit = 0xffff; 823 sc->bios.entry = vmf.vmf_ebx; 824 sc->connectmode = APM_PROT32CONNECT; 825 } else { 826 /* use 16-bit connection */ 827 vmf.vmf_ah = APM_BIOS; 828 vmf.vmf_al = APM_PROT16CONNECT; 829 vmf.vmf_bx = 0; 830 if (vm86_intcall(APM_INT, &vmf)) { 831 printf("apm: 16-bit connection error.\n"); 832 return (ENXIO); 833 } 834 sc->bios.seg.code16.base = (vmf.vmf_ax << 4) + APM_KERNBASE; 835 sc->bios.seg.code16.limit = 0xffff; 836 sc->bios.seg.data.base = (vmf.vmf_cx << 4) + APM_KERNBASE; 837 sc->bios.seg.data.limit = 0xffff; 838 sc->bios.entry = vmf.vmf_bx; 839 sc->connectmode = APM_PROT16CONNECT; 840 } 841 return(0); 842} 843 844 845/* 846 * return 0 if the user will notice and handle the event, 847 * return 1 if the kernel driver should do so. 848 */ 849static int 850apm_record_event(struct apm_softc *sc, u_int event_type) 851{ 852 struct apm_event_info *evp; 853 854 if ((sc->sc_flags & SCFLAG_OPEN) == 0) 855 return 1; /* no user waiting */ 856 if (sc->event_count == APM_NEVENTS) 857 return 1; /* overflow */ 858 if (sc->event_filter[event_type] == 0) 859 return 1; /* not registered */ 860 evp = &sc->event_list[sc->event_ptr]; 861 sc->event_count++; 862 sc->event_ptr++; 863 sc->event_ptr %= APM_NEVENTS; 864 evp->type = event_type; 865 evp->index = ++apm_evindex; 866 selwakeup(&sc->sc_rsel); 867 return (sc->sc_flags & SCFLAG_OCTL) ? 0 : 1; /* user may handle */ 868} 869 870/* Process APM event */ 871static void 872apm_processevent(void) 873{ 874 int apm_event; 875 struct apm_softc *sc = &apm_softc; 876 877#define OPMEV_DEBUGMESSAGE(symbol) case symbol: \ 878 APM_DPRINT("Received APM Event: " #symbol "\n"); 879 880 do { 881 apm_event = apm_getevent(); 882 switch (apm_event) { 883 OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); 884 if (apm_op_inprog == 0) { 885 apm_op_inprog++; 886 if (apm_record_event(sc, apm_event)) { 887 apm_suspend(PMST_STANDBY); 888 } 889 } 890 break; 891 OPMEV_DEBUGMESSAGE(PMEV_USERSTANDBYREQ); 892 if (apm_op_inprog == 0) { 893 apm_op_inprog++; 894 if (apm_record_event(sc, apm_event)) { 895 apm_suspend(PMST_STANDBY); 896 } 897 } 898 break; 899 OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); 900 apm_lastreq_notify(); 901 if (apm_op_inprog == 0) { 902 apm_op_inprog++; 903 if (apm_record_event(sc, apm_event)) { 904 apm_do_suspend(); 905 } 906 } 907 return; /* XXX skip the rest */ 908 OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ); 909 apm_lastreq_notify(); 910 if (apm_op_inprog == 0) { 911 apm_op_inprog++; 912 if (apm_record_event(sc, apm_event)) { 913 apm_do_suspend(); 914 } 915 } 916 return; /* XXX skip the rest */ 917 OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND); 918 apm_do_suspend(); 919 break; 920 OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME); 921 apm_record_event(sc, apm_event); 922 apm_resume(); 923 break; 924 OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME); 925 apm_record_event(sc, apm_event); 926 apm_resume(); 927 break; 928 OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); 929 apm_record_event(sc, apm_event); 930 break; 931 OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); 932 if (apm_record_event(sc, apm_event)) { 933 apm_battery_low(); 934 apm_suspend(PMST_SUSPEND); 935 } 936 break; 937 OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); 938 apm_record_event(sc, apm_event); 939 break; 940 OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME); 941 apm_record_event(sc, apm_event); 942 inittodr(0); /* adjust time to RTC */ 943 break; 944 OPMEV_DEBUGMESSAGE(PMEV_CAPABILITIESCHANGE); 945 apm_record_event(sc, apm_event); 946 break; 947 case PMEV_NOEVENT: 948 break; 949 default: 950 printf("Unknown Original APM Event 0x%x\n", apm_event); 951 break; 952 } 953 } while (apm_event != PMEV_NOEVENT); 954} 955 956/* 957 * Attach APM: 958 * 959 * Initialize APM driver 960 */ 961 962static int 963apm_attach(device_t dev) 964{ 965 struct apm_softc *sc = &apm_softc; 966 int flags; 967 int drv_version; 968 969 if (resource_int_value("apm", 0, "flags", &flags) != 0) 970 flags = 0; 971 972 if (flags & 0x20) 973 statclock_disable = 1; 974 975 sc->initialized = 0; 976 977 /* Must be externally enabled */ 978 sc->active = 0; 979 980 /* Always call HLT in idle loop */ 981 sc->always_halt_cpu = 1; 982 983 getenv_int("debug.apm_debug", &apm_debug); 984 985 /* print bootstrap messages */ 986 APM_DPRINT("apm: APM BIOS version %04lx\n", apm_version); 987 APM_DPRINT("apm: Code16 0x%08x, Data 0x%08x\n", 988 sc->bios.seg.code16.base, sc->bios.seg.data.base); 989 APM_DPRINT("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n", 990 sc->bios.entry, is_enabled(sc->slow_idle_cpu), 991 is_enabled(!sc->disabled)); 992 APM_DPRINT("apm: CS_limit=0x%x, DS_limit=0x%x\n", 993 sc->bios.seg.code16.limit, sc->bios.seg.data.limit); 994 995 /* 996 * In one test, apm bios version was 1.02; an attempt to register 997 * a 1.04 driver resulted in a 1.00 connection! Registering a 998 * 1.02 driver resulted in a 1.02 connection. 999 */ 1000 drv_version = apm_version > 0x102 ? 0x102 : apm_version; 1001 for (; drv_version > 0x100; drv_version--) 1002 if (apm_driver_version(drv_version) == 0) 1003 break; 1004 sc->minorversion = ((drv_version & 0x00f0) >> 4) * 10 + 1005 ((drv_version & 0x000f) >> 0); 1006 sc->majorversion = ((drv_version & 0xf000) >> 12) * 10 + 1007 ((apm_version & 0x0f00) >> 8); 1008 1009 sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); 1010 1011 if (sc->intversion >= INTVERSION(1, 1)) 1012 APM_DPRINT("apm: Engaged control %s\n", is_enabled(!sc->disengaged)); 1013 device_printf(dev, "found APM BIOS v%ld.%ld, connected at v%d.%d\n", 1014 ((apm_version & 0xf000) >> 12) * 10 + ((apm_version & 0x0f00) >> 8), 1015 ((apm_version & 0x00f0) >> 4) * 10 + ((apm_version & 0x000f) >> 0), 1016 sc->majorversion, sc->minorversion); 1017 1018 1019 APM_DPRINT("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu)); 1020 /* enable power management */ 1021 if (sc->disabled) { 1022 if (apm_enable_disable_pm(1)) { 1023 APM_DPRINT("apm: *Warning* enable function failed! [%x]\n", 1024 (sc->bios.r.eax >> 8) & 0xff); 1025 } 1026 } 1027 1028 /* engage power managment (APM 1.1 or later) */ 1029 if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) { 1030 if (apm_engage_disengage_pm(1)) { 1031 APM_DPRINT("apm: *Warning* engage function failed err=[%x]", 1032 (sc->bios.r.eax >> 8) & 0xff); 1033 APM_DPRINT(" (Docked or using external power?).\n"); 1034 } 1035 } 1036 1037 /* Power the system off using APM */ 1038 EVENTHANDLER_REGISTER(shutdown_final, apm_power_off, NULL, 1039 SHUTDOWN_PRI_LAST); 1040 1041 sc->initialized = 1; 1042 sc->suspending = 0; 1043 1044 make_dev(&apm_cdevsw, 0, 0, 5, 0660, "apm"); 1045 make_dev(&apm_cdevsw, 8, 0, 5, 0660, "apmctl"); 1046 return 0; 1047} 1048 1049static int 1050apmopen(dev_t dev, int flag, int fmt, struct proc *p) 1051{ 1052 struct apm_softc *sc = &apm_softc; 1053 int ctl = APMDEV(dev); 1054 1055 if (!sc->initialized) 1056 return (ENXIO); 1057 1058 switch (ctl) { 1059 case APMDEV_CTL: 1060 if (!(flag & FWRITE)) 1061 return EINVAL; 1062 if (sc->sc_flags & SCFLAG_OCTL) 1063 return EBUSY; 1064 sc->sc_flags |= SCFLAG_OCTL; 1065 bzero(sc->event_filter, sizeof sc->event_filter); 1066 break; 1067 case APMDEV_NORMAL: 1068 sc->sc_flags |= SCFLAG_ONORMAL; 1069 break; 1070 default: 1071 return ENXIO; 1072 break; 1073 } 1074 return 0; 1075} 1076 1077static int 1078apmclose(dev_t dev, int flag, int fmt, struct proc *p) 1079{ 1080 struct apm_softc *sc = &apm_softc; 1081 int ctl = APMDEV(dev); 1082 1083 switch (ctl) { 1084 case APMDEV_CTL: 1085 apm_lastreq_rejected(); 1086 sc->sc_flags &= ~SCFLAG_OCTL; 1087 bzero(sc->event_filter, sizeof sc->event_filter); 1088 break; 1089 case APMDEV_NORMAL: 1090 sc->sc_flags &= ~SCFLAG_ONORMAL; 1091 break; 1092 } 1093 if ((sc->sc_flags & SCFLAG_OPEN) == 0) { 1094 sc->event_count = 0; 1095 sc->event_ptr = 0; 1096 } 1097 return 0; 1098} 1099 1100static int 1101apmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 1102{ 1103 struct apm_softc *sc = &apm_softc; 1104 struct apm_bios_arg *args; 1105 int error = 0; 1106 int ret; 1107 int newstate; 1108 1109 if (!sc->initialized) 1110 return (ENXIO); 1111 APM_DPRINT("APM ioctl: cmd = 0x%lx\n", cmd); 1112 switch (cmd) { 1113 case APMIO_SUSPEND: 1114 if (!(flag & FWRITE)) 1115 return (EPERM); 1116 if (sc->active) 1117 apm_suspend(PMST_SUSPEND); 1118 else 1119 error = EINVAL; 1120 break; 1121 1122 case APMIO_STANDBY: 1123 if (!(flag & FWRITE)) 1124 return (EPERM); 1125 if (sc->active) 1126 apm_suspend(PMST_STANDBY); 1127 else 1128 error = EINVAL; 1129 break; 1130 1131 case APMIO_GETINFO_OLD: 1132 { 1133 struct apm_info info; 1134 apm_info_old_t aiop; 1135 1136 if (apm_get_info(&info)) 1137 error = ENXIO; 1138 aiop = (apm_info_old_t)addr; 1139 aiop->ai_major = info.ai_major; 1140 aiop->ai_minor = info.ai_minor; 1141 aiop->ai_acline = info.ai_acline; 1142 aiop->ai_batt_stat = info.ai_batt_stat; 1143 aiop->ai_batt_life = info.ai_batt_life; 1144 aiop->ai_status = info.ai_status; 1145 } 1146 break; 1147 case APMIO_GETINFO: 1148 if (apm_get_info((apm_info_t)addr)) 1149 error = ENXIO; 1150 break; 1151 case APMIO_GETPWSTATUS: 1152 if (apm_get_pwstatus((apm_pwstatus_t)addr)) 1153 error = ENXIO; 1154 break; 1155 case APMIO_ENABLE: 1156 if (!(flag & FWRITE)) 1157 return (EPERM); 1158 apm_event_enable(); 1159 break; 1160 case APMIO_DISABLE: 1161 if (!(flag & FWRITE)) 1162 return (EPERM); 1163 apm_event_disable(); 1164 break; 1165 case APMIO_HALTCPU: 1166 if (!(flag & FWRITE)) 1167 return (EPERM); 1168 apm_halt_cpu(); 1169 break; 1170 case APMIO_NOTHALTCPU: 1171 if (!(flag & FWRITE)) 1172 return (EPERM); 1173 apm_not_halt_cpu(); 1174 break; 1175 case APMIO_DISPLAY: 1176 if (!(flag & FWRITE)) 1177 return (EPERM); 1178 newstate = *(int *)addr; 1179 if (apm_display(newstate)) 1180 error = ENXIO; 1181 break; 1182 case APMIO_BIOS: 1183 if (!(flag & FWRITE)) 1184 return (EPERM); 1185 /* XXX compatibility with the old interface */ 1186 args = (struct apm_bios_arg *)addr; 1187 sc->bios.r.eax = args->eax; 1188 sc->bios.r.ebx = args->ebx; 1189 sc->bios.r.ecx = args->ecx; 1190 sc->bios.r.edx = args->edx; 1191 sc->bios.r.esi = args->esi; 1192 sc->bios.r.edi = args->edi; 1193 if ((ret = apm_bioscall())) { 1194 /* 1195 * Return code 1 means bios call was unsuccessful. 1196 * Error code is stored in %ah. 1197 * Return code -1 means bios call was unsupported 1198 * in the APM BIOS version. 1199 */ 1200 if (ret == -1) { 1201 error = EINVAL; 1202 } 1203 } else { 1204 /* 1205 * Return code 0 means bios call was successful. 1206 * We need only %al and can discard %ah. 1207 */ 1208 sc->bios.r.eax &= 0xff; 1209 } 1210 args->eax = sc->bios.r.eax; 1211 args->ebx = sc->bios.r.ebx; 1212 args->ecx = sc->bios.r.ecx; 1213 args->edx = sc->bios.r.edx; 1214 args->esi = sc->bios.r.esi; 1215 args->edi = sc->bios.r.edi; 1216 break; 1217 default: 1218 error = EINVAL; 1219 break; 1220 } 1221 1222 /* for /dev/apmctl */ 1223 if (APMDEV(dev) == APMDEV_CTL) { 1224 struct apm_event_info *evp; 1225 int i; 1226 1227 error = 0; 1228 switch (cmd) { 1229 case APMIO_NEXTEVENT: 1230 if (!sc->event_count) { 1231 error = EAGAIN; 1232 } else { 1233 evp = (struct apm_event_info *)addr; 1234 i = sc->event_ptr + APM_NEVENTS - sc->event_count; 1235 i %= APM_NEVENTS; 1236 *evp = sc->event_list[i]; 1237 sc->event_count--; 1238 } 1239 break; 1240 case APMIO_REJECTLASTREQ: 1241 if (apm_lastreq_rejected()) { 1242 error = EINVAL; 1243 } 1244 break; 1245 default: 1246 error = EINVAL; 1247 break; 1248 } 1249 } 1250 1251 return error; 1252} 1253 1254static int 1255apmwrite(dev_t dev, struct uio *uio, int ioflag) 1256{ 1257 struct apm_softc *sc = &apm_softc; 1258 u_int event_type; 1259 int error; 1260 u_char enabled; 1261 1262 if (APMDEV(dev) != APMDEV_CTL) 1263 return(ENODEV); 1264 if (uio->uio_resid != sizeof(u_int)) 1265 return(E2BIG); 1266 1267 if ((error = uiomove((caddr_t)&event_type, sizeof(u_int), uio))) 1268 return(error); 1269 1270 if (event_type < 0 || event_type >= APM_NPMEV) 1271 return(EINVAL); 1272 1273 if (sc->event_filter[event_type] == 0) { 1274 enabled = 1; 1275 } else { 1276 enabled = 0; 1277 } 1278 sc->event_filter[event_type] = enabled; 1279 APM_DPRINT("apmwrite: event 0x%x %s\n", event_type, is_enabled(enabled)); 1280 1281 return uio->uio_resid; 1282} 1283 1284static int 1285apmpoll(dev_t dev, int events, struct proc *p) 1286{ 1287 struct apm_softc *sc = &apm_softc; 1288 int revents = 0; 1289 1290 if (events & (POLLIN | POLLRDNORM)) { 1291 if (sc->event_count) { 1292 revents |= events & (POLLIN | POLLRDNORM); 1293 } else { 1294 selrecord(p, &sc->sc_rsel); 1295 } 1296 } 1297 1298 return (revents); 1299} 1300 1301static device_method_t apm_methods[] = { 1302 /* Device interface */ 1303 DEVMETHOD(device_identify, apm_identify), 1304 DEVMETHOD(device_probe, apm_probe), 1305 DEVMETHOD(device_attach, apm_attach), 1306 1307 { 0, 0 } 1308}; 1309 1310static driver_t apm_driver = { 1311 "apm", 1312 apm_methods, 1313 1, /* no softc (XXX) */ 1314}; 1315 1316static devclass_t apm_devclass; 1317 1318DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, 0, 0); 1319