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