1 /* $NetBSD: apmdev.c,v 1.20 2009/03/30 06:22:25 uwe Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by John Kohl and Christopher G. Demetriou. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 /* 32 * from: sys/arch/i386/i386/apm.c,v 1.49 2000/05/08 33 */ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: apmdev.c,v 1.20 2009/03/30 06:22:25 uwe Exp $"); 37 38 #ifdef _KERNEL_OPT 39 #include "opt_apmdev.h" 40 #endif 41 42 #ifdef APM_NOIDLE 43 #error APM_NOIDLE option deprecated; use APM_NO_IDLE instead 44 #endif 45 46 #if defined(DEBUG) && !defined(APMDEBUG) 47 #define APMDEBUG 48 #endif 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/signalvar.h> 53 #include <sys/kernel.h> 54 #include <sys/proc.h> 55 #include <sys/kthread.h> 56 #include <sys/user.h> 57 #include <sys/malloc.h> 58 #include <sys/device.h> 59 #include <sys/fcntl.h> 60 #include <sys/ioctl.h> 61 #include <sys/select.h> 62 #include <sys/poll.h> 63 #include <sys/conf.h> 64 65 #include <dev/hpc/apm/apmvar.h> 66 67 #include <machine/stdarg.h> 68 69 #ifdef APMDEBUG 70 #define DPRINTF(f, x) do { if (apmdebug & (f)) printf x; } while (0) 71 72 73 #ifdef APMDEBUG_VALUE 74 int apmdebug = APMDEBUG_VALUE; 75 #else 76 int apmdebug = 0; 77 #endif /* APMDEBUG_VALUE */ 78 79 #else 80 #define DPRINTF(f, x) /**/ 81 #endif /* APMDEBUG */ 82 83 #define APM_NEVENTS 16 84 85 struct apm_softc { 86 device_t sc_dev; 87 struct selinfo sc_rsel; 88 struct selinfo sc_xsel; 89 int sc_flags; 90 int sc_event_count; 91 int sc_event_ptr; 92 int sc_power_state; 93 lwp_t *sc_thread; 94 kmutex_t sc_lock; 95 struct apm_event_info sc_event_list[APM_NEVENTS]; 96 struct apm_accessops *sc_ops; 97 int sc_vers; 98 int sc_detail; 99 void *sc_cookie; 100 }; 101 #define SCFLAG_OREAD 0x0000001 102 #define SCFLAG_OWRITE 0x0000002 103 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) 104 105 #define APMUNIT(dev) (minor(dev)&0xf0) 106 #define APMDEV(dev) (minor(dev)&0x0f) 107 #define APMDEV_NORMAL 0 108 #define APMDEV_CTL 8 109 110 /* 111 * A brief note on the locking protocol: it's very simple; we 112 * assert an exclusive lock any time thread context enters the 113 * APM module. This is both the APM thread itself, as well as 114 * user context. 115 */ 116 #define APM_LOCK(apmsc) \ 117 (void) mutex_enter(&(apmsc)->sc_lock) 118 #define APM_UNLOCK(apmsc) \ 119 (void) mutex_exit(&(apmsc)->sc_lock) 120 121 static void apmattach(device_t, device_t, void *); 122 static int apmmatch(device_t, cfdata_t, void *); 123 124 static void apm_event_handle(struct apm_softc *, u_int, u_int); 125 static void apm_periodic_check(struct apm_softc *); 126 static void apm_thread(void *); 127 static void apm_perror(const char *, int, ...) 128 __attribute__((__format__(__printf__,1,3))); 129 #ifdef APM_POWER_PRINT 130 static void apm_power_print(struct apm_softc *, struct apm_power_info *); 131 #endif 132 static int apm_record_event(struct apm_softc *, u_int); 133 static void apm_set_ver(struct apm_softc *); 134 static void apm_standby(struct apm_softc *); 135 static const char *apm_strerror(int); 136 static void apm_suspend(struct apm_softc *); 137 static void apm_resume(struct apm_softc *, u_int, u_int); 138 139 CFATTACH_DECL_NEW(apmdev, sizeof(struct apm_softc), 140 apmmatch, apmattach, NULL, NULL); 141 142 extern struct cfdriver apmdev_cd; 143 144 dev_type_open(apmdevopen); 145 dev_type_close(apmdevclose); 146 dev_type_ioctl(apmdevioctl); 147 dev_type_poll(apmdevpoll); 148 dev_type_kqfilter(apmdevkqfilter); 149 150 const struct cdevsw apmdev_cdevsw = { 151 apmdevopen, apmdevclose, noread, nowrite, apmdevioctl, 152 nostop, notty, apmdevpoll, nommap, apmdevkqfilter, D_OTHER 153 }; 154 155 /* configurable variables */ 156 int apm_bogus_bios = 0; 157 #ifdef APM_DISABLE 158 int apm_enabled = 0; 159 #else 160 int apm_enabled = 1; 161 #endif 162 #ifdef APM_NO_IDLE 163 int apm_do_idle = 0; 164 #else 165 int apm_do_idle = 1; 166 #endif 167 #ifdef APM_NO_STANDBY 168 int apm_do_standby = 0; 169 #else 170 int apm_do_standby = 1; 171 #endif 172 #ifdef APM_V10_ONLY 173 int apm_v11_enabled = 0; 174 #else 175 int apm_v11_enabled = 1; 176 #endif 177 #ifdef APM_NO_V12 178 int apm_v12_enabled = 0; 179 #else 180 int apm_v12_enabled = 1; 181 #endif 182 183 /* variables used during operation (XXX cgd) */ 184 u_char apm_majver, apm_minver; 185 int apm_inited; 186 int apm_standbys, apm_userstandbys, apm_suspends, apm_battlow; 187 int apm_damn_fool_bios, apm_op_inprog; 188 int apm_evindex; 189 190 static int apm_spl; /* saved spl while suspended */ 191 192 static const char * 193 apm_strerror(int code) 194 { 195 switch (code) { 196 case APM_ERR_PM_DISABLED: 197 return ("power management disabled"); 198 case APM_ERR_REALALREADY: 199 return ("real mode interface already connected"); 200 case APM_ERR_NOTCONN: 201 return ("interface not connected"); 202 case APM_ERR_16ALREADY: 203 return ("16-bit interface already connected"); 204 case APM_ERR_16NOTSUPP: 205 return ("16-bit interface not supported"); 206 case APM_ERR_32ALREADY: 207 return ("32-bit interface already connected"); 208 case APM_ERR_32NOTSUPP: 209 return ("32-bit interface not supported"); 210 case APM_ERR_UNRECOG_DEV: 211 return ("unrecognized device ID"); 212 case APM_ERR_ERANGE: 213 return ("parameter out of range"); 214 case APM_ERR_NOTENGAGED: 215 return ("interface not engaged"); 216 case APM_ERR_UNABLE: 217 return ("unable to enter requested state"); 218 case APM_ERR_NOEVENTS: 219 return ("no pending events"); 220 case APM_ERR_NOT_PRESENT: 221 return ("no APM present"); 222 default: 223 return ("unknown error code"); 224 } 225 } 226 227 static void 228 apm_perror(const char *str, int errinfo, ...) /* XXX cgd */ 229 { 230 va_list ap; 231 232 printf("APM "); 233 234 va_start(ap, errinfo); 235 vprintf(str, ap); /* XXX cgd */ 236 va_end(ap); 237 238 printf(": %s\n", apm_strerror(errinfo)); 239 } 240 241 #ifdef APM_POWER_PRINT 242 static void 243 apm_power_print(struct apm_softc *sc, struct apm_power_info *pi) 244 { 245 246 if (pi->battery_life != APM_BATT_LIFE_UNKNOWN) { 247 aprint_normal_dev(sc->sc_dev, 248 "battery life expectancy: %d%%\n", 249 pi->battery_life); 250 } 251 aprint_normal_dev(sc->sc_dev, "A/C state: "); 252 switch (pi->ac_state) { 253 case APM_AC_OFF: 254 printf("off\n"); 255 break; 256 case APM_AC_ON: 257 printf("on\n"); 258 break; 259 case APM_AC_BACKUP: 260 printf("backup power\n"); 261 break; 262 default: 263 case APM_AC_UNKNOWN: 264 printf("unknown\n"); 265 break; 266 } 267 aprint_normal_dev(sc->sc_dev, "battery charge state:"); 268 if (apm_minver == 0) 269 switch (pi->battery_state) { 270 case APM_BATT_HIGH: 271 printf("high\n"); 272 break; 273 case APM_BATT_LOW: 274 printf("low\n"); 275 break; 276 case APM_BATT_CRITICAL: 277 printf("critical\n"); 278 break; 279 case APM_BATT_CHARGING: 280 printf("charging\n"); 281 break; 282 case APM_BATT_UNKNOWN: 283 printf("unknown\n"); 284 break; 285 default: 286 printf("undecoded state %x\n", pi->battery_state); 287 break; 288 } 289 else if (apm_minver >= 1) { 290 if (pi->battery_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) 291 printf(" no battery"); 292 else { 293 if (pi->battery_flags & APM_BATT_FLAG_HIGH) 294 printf(" high"); 295 if (pi->battery_flags & APM_BATT_FLAG_LOW) 296 printf(" low"); 297 if (pi->battery_flags & APM_BATT_FLAG_CRITICAL) 298 printf(" critical"); 299 if (pi->battery_flags & APM_BATT_FLAG_CHARGING) 300 printf(" charging"); 301 } 302 printf("\n"); 303 if (pi->minutes_valid) { 304 aprint_normal_dev(sc->sc_dev, "estimated "); 305 if (pi->minutes_left / 60) 306 printf("%dh ", pi->minutes_left / 60); 307 printf("%dm\n", pi->minutes_left % 60); 308 } 309 } 310 return; 311 } 312 #endif 313 314 static void 315 apm_suspend(struct apm_softc *sc) 316 { 317 int error; 318 319 if (sc->sc_power_state == PWR_SUSPEND) { 320 #ifdef APMDEBUG 321 aprint_debug_dev(sc->sc_dev, 322 "apm_suspend: already suspended?\n"); 323 #endif 324 return; 325 } 326 sc->sc_power_state = PWR_SUSPEND; 327 328 dopowerhooks(PWR_SOFTSUSPEND); 329 (void) tsleep(sc, PWAIT, "apmsuspend", hz/2); 330 331 apm_spl = splhigh(); 332 333 dopowerhooks(PWR_SUSPEND); 334 335 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 336 APM_SYS_SUSPEND); 337 } 338 339 static void 340 apm_standby(struct apm_softc *sc) 341 { 342 int error; 343 344 if (sc->sc_power_state == PWR_STANDBY) { 345 #ifdef APMDEBUG 346 aprint_debug_dev(sc->sc_dev, 347 "apm_standby: already standing by?\n"); 348 #endif 349 return; 350 } 351 sc->sc_power_state = PWR_STANDBY; 352 353 dopowerhooks(PWR_SOFTSTANDBY); 354 (void) tsleep(sc, PWAIT, "apmstandby", hz/2); 355 356 apm_spl = splhigh(); 357 358 dopowerhooks(PWR_STANDBY); 359 360 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 361 APM_SYS_STANDBY); 362 } 363 364 static void 365 apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info) 366 { 367 368 if (sc->sc_power_state == PWR_RESUME) { 369 #ifdef APMDEBUG 370 aprint_debug_dev(sc->sc_dev, "apm_resume: already running?\n"); 371 #endif 372 return; 373 } 374 sc->sc_power_state = PWR_RESUME; 375 376 #if 0 /* XXX: def TIME_FREQ */ 377 /* 378 * Some system requires its clock to be initialized after hybernation. 379 */ 380 initrtclock(TIMER_FREQ); 381 #endif 382 383 inittodr(time_second); 384 dopowerhooks(PWR_RESUME); 385 386 splx(apm_spl); 387 388 dopowerhooks(PWR_SOFTRESUME); 389 390 apm_record_event(sc, event_type); 391 } 392 393 /* 394 * return 0 if the user will notice and handle the event, 395 * return 1 if the kernel driver should do so. 396 */ 397 static int 398 apm_record_event(struct apm_softc *sc, u_int event_type) 399 { 400 struct apm_event_info *evp; 401 402 if ((sc->sc_flags & SCFLAG_OPEN) == 0) 403 return 1; /* no user waiting */ 404 if (sc->sc_event_count == APM_NEVENTS) 405 return 1; /* overflow */ 406 evp = &sc->sc_event_list[sc->sc_event_ptr]; 407 sc->sc_event_count++; 408 sc->sc_event_ptr++; 409 sc->sc_event_ptr %= APM_NEVENTS; 410 evp->type = event_type; 411 evp->index = ++apm_evindex; 412 selnotify(&sc->sc_rsel, 0, 0); 413 return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */ 414 } 415 416 static void 417 apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info) 418 { 419 int error; 420 const char *code; 421 struct apm_power_info pi; 422 423 switch (event_code) { 424 case APM_USER_STANDBY_REQ: 425 DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n")); 426 if (apm_do_standby) { 427 if (apm_record_event(sc, event_code)) 428 apm_userstandbys++; 429 apm_op_inprog++; 430 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 431 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 432 } else { 433 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 434 APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED); 435 /* in case BIOS hates being spurned */ 436 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1); 437 } 438 break; 439 440 case APM_STANDBY_REQ: 441 DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n")); 442 if (apm_standbys || apm_suspends) { 443 DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM, 444 ("damn fool BIOS did not wait for answer\n")); 445 /* just give up the fight */ 446 apm_damn_fool_bios = 1; 447 } 448 if (apm_do_standby) { 449 if (apm_record_event(sc, event_code)) 450 apm_standbys++; 451 apm_op_inprog++; 452 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 453 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 454 } else { 455 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 456 APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED); 457 /* in case BIOS hates being spurned */ 458 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1); 459 } 460 break; 461 462 case APM_USER_SUSPEND_REQ: 463 DPRINTF(APMDEBUG_EVENTS, ("apmev: user suspend request\n")); 464 if (apm_record_event(sc, event_code)) 465 apm_suspends++; 466 apm_op_inprog++; 467 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 468 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 469 break; 470 471 case APM_SUSPEND_REQ: 472 DPRINTF(APMDEBUG_EVENTS, ("apmev: system suspend request\n")); 473 if (apm_standbys || apm_suspends) { 474 DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM, 475 ("damn fool BIOS did not wait for answer\n")); 476 /* just give up the fight */ 477 apm_damn_fool_bios = 1; 478 } 479 if (apm_record_event(sc, event_code)) 480 apm_suspends++; 481 apm_op_inprog++; 482 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 483 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 484 break; 485 486 case APM_POWER_CHANGE: 487 DPRINTF(APMDEBUG_EVENTS, ("apmev: power status change\n")); 488 error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, &pi); 489 #ifdef APM_POWER_PRINT 490 /* only print if nobody is catching events. */ 491 if (error == 0 && 492 (sc->sc_flags & (SCFLAG_OREAD|SCFLAG_OWRITE)) == 0) 493 apm_power_print(sc, &pi); 494 #endif 495 apm_record_event(sc, event_code); 496 break; 497 498 case APM_NORMAL_RESUME: 499 DPRINTF(APMDEBUG_EVENTS, ("apmev: resume system\n")); 500 apm_resume(sc, event_code, event_info); 501 break; 502 503 case APM_CRIT_RESUME: 504 DPRINTF(APMDEBUG_EVENTS, ("apmev: critical resume system")); 505 apm_resume(sc, event_code, event_info); 506 break; 507 508 case APM_SYS_STANDBY_RESUME: 509 DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby resume\n")); 510 apm_resume(sc, event_code, event_info); 511 break; 512 513 case APM_UPDATE_TIME: 514 DPRINTF(APMDEBUG_EVENTS, ("apmev: update time\n")); 515 apm_resume(sc, event_code, event_info); 516 break; 517 518 case APM_CRIT_SUSPEND_REQ: 519 DPRINTF(APMDEBUG_EVENTS, ("apmev: critical system suspend\n")); 520 apm_record_event(sc, event_code); 521 apm_suspend(sc); 522 break; 523 524 case APM_BATTERY_LOW: 525 DPRINTF(APMDEBUG_EVENTS, ("apmev: battery low\n")); 526 apm_battlow++; 527 apm_record_event(sc, event_code); 528 break; 529 530 case APM_CAP_CHANGE: 531 DPRINTF(APMDEBUG_EVENTS, ("apmev: capability change\n")); 532 if (apm_minver < 2) { 533 DPRINTF(APMDEBUG_EVENTS, ("apm: unexpected event\n")); 534 } else { 535 u_int numbatts, capflags; 536 (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, 537 &numbatts, &capflags); 538 (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, &pi); 539 } 540 break; 541 542 default: 543 switch (event_code >> 8) { 544 case 0: 545 code = "reserved system"; 546 break; 547 case 1: 548 code = "reserved device"; 549 break; 550 case 2: 551 code = "OEM defined"; 552 break; 553 default: 554 code = "reserved"; 555 break; 556 } 557 printf("APM: %s event code %x\n", code, event_code); 558 } 559 } 560 561 static void 562 apm_periodic_check(struct apm_softc *sc) 563 { 564 int error; 565 u_int event_code, event_info; 566 567 568 /* 569 * tell the BIOS we're working on it, if asked to do a 570 * suspend/standby 571 */ 572 if (apm_op_inprog) 573 (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 574 APM_LASTREQ_INPROG); 575 576 while ((error = (*sc->sc_ops->aa_get_event)(sc->sc_cookie, &event_code, 577 &event_info)) == 0 && !apm_damn_fool_bios) 578 apm_event_handle(sc, event_code, event_info); 579 580 if (error != APM_ERR_NOEVENTS) 581 apm_perror("get event", error); 582 if (apm_suspends) { 583 apm_op_inprog = 0; 584 apm_suspend(sc); 585 } else if (apm_standbys || apm_userstandbys) { 586 apm_op_inprog = 0; 587 apm_standby(sc); 588 } 589 apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0; 590 apm_damn_fool_bios = 0; 591 } 592 593 static void 594 apm_set_ver(struct apm_softc *sc) 595 { 596 597 if (apm_v12_enabled && 598 APM_MAJOR_VERS(sc->sc_vers) == 1 && 599 APM_MINOR_VERS(sc->sc_vers) == 2) { 600 apm_majver = 1; 601 apm_minver = 2; 602 goto ok; 603 } 604 605 if (apm_v11_enabled && 606 APM_MAJOR_VERS(sc->sc_vers) == 1 && 607 APM_MINOR_VERS(sc->sc_vers) == 1) { 608 apm_majver = 1; 609 apm_minver = 1; 610 } else { 611 apm_majver = 1; 612 apm_minver = 0; 613 } 614 ok: 615 aprint_normal("Power Management spec V%d.%d", apm_majver, apm_minver); 616 apm_inited = 1; 617 if (sc->sc_detail & APM_IDLE_SLOWS) { 618 #ifdef DIAGNOSTIC 619 /* not relevant often */ 620 aprint_normal(" (slowidle)"); 621 #endif 622 /* leave apm_do_idle at its user-configured setting */ 623 } else 624 apm_do_idle = 0; 625 #ifdef DIAGNOSTIC 626 if (sc->sc_detail & APM_BIOS_PM_DISABLED) 627 aprint_normal(" (BIOS mgmt disabled)"); 628 if (sc->sc_detail & APM_BIOS_PM_DISENGAGED) 629 aprint_normal(" (BIOS managing devices)"); 630 #endif 631 } 632 633 static int 634 apmmatch(device_t parent, cfdata_t match, void *aux) 635 { 636 static int got; 637 return !got++; 638 } 639 640 static void 641 apmattach(device_t parent, device_t self, void *aux) 642 { 643 struct apm_softc *sc; 644 struct apmdev_attach_args *aaa = aux; 645 struct apm_power_info pinfo; 646 u_int numbatts, capflags; 647 int error; 648 649 sc = device_private(self); 650 sc->sc_dev = self; 651 652 sc->sc_detail = aaa->apm_detail; 653 sc->sc_vers = aaa->apm_detail & 0xffff; /* XXX: magic */ 654 655 sc->sc_ops = aaa->accessops; 656 sc->sc_cookie = aaa->accesscookie; 657 658 aprint_naive("\n"); 659 aprint_normal(": "); 660 661 switch ((APM_MAJOR_VERS(sc->sc_vers) << 8) + APM_MINOR_VERS(sc->sc_vers)) { 662 case 0x0100: 663 apm_v11_enabled = 0; 664 apm_v12_enabled = 0; 665 break; 666 case 0x0101: 667 apm_v12_enabled = 0; 668 /* fall through */ 669 case 0x0102: 670 default: 671 break; 672 } 673 674 apm_set_ver(sc); /* prints version info */ 675 aprint_normal("\n"); 676 if (apm_minver >= 2) 677 (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, &numbatts, 678 &capflags); 679 680 /* 681 * enable power management 682 */ 683 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1); 684 685 error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, &pinfo); 686 if (error == 0) { 687 #ifdef APM_POWER_PRINT 688 apm_power_print(sc, &pinfo); 689 #endif 690 } else 691 apm_perror("get power status", error); 692 693 if (sc->sc_ops->aa_cpu_busy) 694 (*sc->sc_ops->aa_cpu_busy)(sc->sc_cookie); 695 696 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 697 698 /* Initial state is `resumed'. */ 699 sc->sc_power_state = PWR_RESUME; 700 selinit(&sc->sc_rsel); 701 selinit(&sc->sc_xsel); 702 703 /* Do an initial check. */ 704 apm_periodic_check(sc); 705 706 /* 707 * Create a kernel thread to periodically check for APM events, 708 * and notify other subsystems when they occur. 709 */ 710 if (kthread_create(PRI_NONE, 0, NULL, apm_thread, sc, 711 &sc->sc_thread, "%s", device_xname(sc->sc_dev)) != 0) { 712 /* 713 * We were unable to create the APM thread; bail out. 714 */ 715 if (sc->sc_ops->aa_disconnect) 716 (*sc->sc_ops->aa_disconnect)(sc->sc_cookie); 717 aprint_error_dev(sc->sc_dev, "unable to create thread, " 718 "kernel APM support disabled\n"); 719 } 720 } 721 722 /* 723 * Print function (for parent devices). 724 */ 725 int 726 apmprint(void *aux, const char *pnp) 727 { 728 if (pnp) 729 aprint_normal("apm at %s", pnp); 730 731 return (UNCONF); 732 } 733 734 void 735 apm_thread(void *arg) 736 { 737 struct apm_softc *apmsc = arg; 738 739 /* 740 * Loop forever, doing a periodic check for APM events. 741 */ 742 for (;;) { 743 APM_LOCK(apmsc); 744 apm_periodic_check(apmsc); 745 APM_UNLOCK(apmsc); 746 (void) tsleep(apmsc, PWAIT, "apmev", (8 * hz) / 7); 747 } 748 } 749 750 int 751 apmdevopen(dev_t dev, int flag, int mode, struct lwp *l) 752 { 753 int ctl = APMDEV(dev); 754 int error = 0; 755 struct apm_softc *sc; 756 757 sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 758 if (!sc) 759 return ENXIO; 760 761 if (!apm_inited) 762 return ENXIO; 763 764 DPRINTF(APMDEBUG_DEVICE, 765 ("apmopen: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode)); 766 767 APM_LOCK(sc); 768 switch (ctl) { 769 case APMDEV_CTL: 770 if (!(flag & FWRITE)) { 771 error = EINVAL; 772 break; 773 } 774 if (sc->sc_flags & SCFLAG_OWRITE) { 775 error = EBUSY; 776 break; 777 } 778 sc->sc_flags |= SCFLAG_OWRITE; 779 break; 780 case APMDEV_NORMAL: 781 if (!(flag & FREAD) || (flag & FWRITE)) { 782 error = EINVAL; 783 break; 784 } 785 sc->sc_flags |= SCFLAG_OREAD; 786 break; 787 default: 788 error = ENXIO; 789 break; 790 } 791 APM_UNLOCK(sc); 792 793 return (error); 794 } 795 796 int 797 apmdevclose(dev_t dev, int flag, int mode, 798 struct lwp *l) 799 { 800 struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 801 int ctl = APMDEV(dev); 802 803 DPRINTF(APMDEBUG_DEVICE, 804 ("apmclose: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode)); 805 806 APM_LOCK(sc); 807 switch (ctl) { 808 case APMDEV_CTL: 809 sc->sc_flags &= ~SCFLAG_OWRITE; 810 break; 811 case APMDEV_NORMAL: 812 sc->sc_flags &= ~SCFLAG_OREAD; 813 break; 814 } 815 if ((sc->sc_flags & SCFLAG_OPEN) == 0) { 816 sc->sc_event_count = 0; 817 sc->sc_event_ptr = 0; 818 } 819 APM_UNLOCK(sc); 820 return 0; 821 } 822 823 int 824 apmdevioctl(dev_t dev, u_long cmd, void *data, int flag, 825 struct lwp *l) 826 { 827 struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 828 struct apm_power_info *powerp; 829 struct apm_event_info *evp; 830 #if 0 831 struct apm_ctl *actl; 832 #endif 833 int i, error = 0; 834 int batt_flags; 835 836 APM_LOCK(sc); 837 switch (cmd) { 838 case APM_IOC_STANDBY: 839 if (!apm_do_standby) { 840 error = EOPNOTSUPP; 841 break; 842 } 843 844 if ((flag & FWRITE) == 0) { 845 error = EBADF; 846 break; 847 } 848 apm_userstandbys++; 849 break; 850 851 case APM_IOC_SUSPEND: 852 if ((flag & FWRITE) == 0) { 853 error = EBADF; 854 break; 855 } 856 apm_suspends++; 857 break; 858 859 case APM_IOC_NEXTEVENT: 860 if (!sc->sc_event_count) 861 error = EAGAIN; 862 else { 863 evp = (struct apm_event_info *)data; 864 i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count; 865 i %= APM_NEVENTS; 866 *evp = sc->sc_event_list[i]; 867 sc->sc_event_count--; 868 } 869 break; 870 871 case OAPM_IOC_GETPOWER: 872 case APM_IOC_GETPOWER: 873 powerp = (struct apm_power_info *)data; 874 if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 875 powerp)) != 0) { 876 apm_perror("ioctl get power status", error); 877 error = EIO; 878 break; 879 } 880 switch (apm_minver) { 881 case 0: 882 break; 883 case 1: 884 default: 885 batt_flags = powerp->battery_flags; 886 powerp->battery_state = APM_BATT_UNKNOWN; 887 if (batt_flags & APM_BATT_FLAG_HIGH) 888 powerp->battery_state = APM_BATT_HIGH; 889 else if (batt_flags & APM_BATT_FLAG_LOW) 890 powerp->battery_state = APM_BATT_LOW; 891 else if (batt_flags & APM_BATT_FLAG_CRITICAL) 892 powerp->battery_state = APM_BATT_CRITICAL; 893 else if (batt_flags & APM_BATT_FLAG_CHARGING) 894 powerp->battery_state = APM_BATT_CHARGING; 895 else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) 896 powerp->battery_state = APM_BATT_ABSENT; 897 break; 898 } 899 break; 900 901 default: 902 error = ENOTTY; 903 } 904 APM_UNLOCK(sc); 905 906 return (error); 907 } 908 909 int 910 apmdevpoll(dev_t dev, int events, struct lwp *l) 911 { 912 struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 913 int revents = 0; 914 915 APM_LOCK(sc); 916 if (events & (POLLIN | POLLRDNORM)) { 917 if (sc->sc_event_count) 918 revents |= events & (POLLIN | POLLRDNORM); 919 else 920 selrecord(l, &sc->sc_rsel); 921 } 922 APM_UNLOCK(sc); 923 924 return (revents); 925 } 926 927 static void 928 filt_apmrdetach(struct knote *kn) 929 { 930 struct apm_softc *sc = kn->kn_hook; 931 932 APM_LOCK(sc); 933 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); 934 APM_UNLOCK(sc); 935 } 936 937 static int 938 filt_apmread(struct knote *kn, long hint) 939 { 940 struct apm_softc *sc = kn->kn_hook; 941 942 kn->kn_data = sc->sc_event_count; 943 return (kn->kn_data > 0); 944 } 945 946 static const struct filterops apmread_filtops = 947 { 1, NULL, filt_apmrdetach, filt_apmread }; 948 949 int 950 apmdevkqfilter(dev_t dev, struct knote *kn) 951 { 952 struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev)); 953 struct klist *klist; 954 955 switch (kn->kn_filter) { 956 case EVFILT_READ: 957 klist = &sc->sc_rsel.sel_klist; 958 kn->kn_fop = &apmread_filtops; 959 break; 960 961 default: 962 return (EINVAL); 963 } 964 965 kn->kn_hook = sc; 966 967 APM_LOCK(sc); 968 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 969 APM_UNLOCK(sc); 970 971 return (0); 972 } 973