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