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