1 /* $NetBSD: apm.c,v 1.12 2007/07/09 22:58:52 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.12 2007/07/09 22:58:52 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/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) lockmgr(&(apmsc)->sc_lock, LK_EXCLUSIVE, NULL) 106 #define APM_UNLOCK(apmsc) \ 107 (void) lockmgr(&(apmsc)->sc_lock, LK_RELEASE, NULL) 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 dopowerhooks(PWR_SOFTSUSPEND); 320 321 apm_spl = splhigh(); 322 323 dopowerhooks(PWR_SUSPEND); 324 } 325 326 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 327 APM_SYS_SUSPEND); 328 329 if (error) 330 apm_resume(sc, 0, 0); 331 } 332 333 static void 334 apm_standby(struct apm_softc *sc) 335 { 336 int error; 337 338 if (sc->sc_power_state == PWR_STANDBY) { 339 #ifdef APMDEBUG 340 printf("%s: apm_standby: already standing by?\n", 341 sc->sc_dev.dv_xname); 342 #endif 343 return; 344 } 345 sc->sc_power_state = PWR_STANDBY; 346 347 if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) { 348 dopowerhooks(PWR_SOFTSTANDBY); 349 350 apm_spl = splhigh(); 351 352 dopowerhooks(PWR_STANDBY); 353 } 354 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS, 355 APM_SYS_STANDBY); 356 if (error) 357 apm_resume(sc, 0, 0); 358 } 359 360 static void 361 apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info) 362 { 363 364 if (sc->sc_power_state == PWR_RESUME) { 365 #ifdef APMDEBUG 366 printf("%s: apm_resume: already running?\n", 367 sc->sc_dev.dv_xname); 368 #endif 369 return; 370 } 371 sc->sc_power_state = PWR_RESUME; 372 373 #ifdef TIMER_FREQ 374 /* 375 * Some system requires its clock to be initialized after hybernation. 376 */ 377 initrtclock(TIMER_FREQ); 378 #endif 379 380 inittodr(time_second); 381 if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) { 382 dopowerhooks(PWR_RESUME); 383 384 splx(apm_spl); 385 386 dopowerhooks(PWR_SOFTRESUME); 387 } 388 389 apm_record_event(sc, event_type); 390 } 391 392 /* 393 * return 0 if the user will notice and handle the event, 394 * return 1 if the kernel driver should do so. 395 */ 396 static int 397 apm_record_event(struct apm_softc *sc, u_int event_type) 398 { 399 struct apm_event_info *evp; 400 401 if ((sc->sc_flags & SCFLAG_OPEN) == 0) 402 return 1; /* no user waiting */ 403 if (sc->sc_event_count == APM_NEVENTS) 404 return 1; /* overflow */ 405 evp = &sc->sc_event_list[sc->sc_event_ptr]; 406 sc->sc_event_count++; 407 sc->sc_event_ptr++; 408 sc->sc_event_ptr %= APM_NEVENTS; 409 evp->type = event_type; 410 evp->index = ++apm_evindex; 411 selnotify(&sc->sc_rsel, 0); 412 return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */ 413 } 414 415 static void 416 apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info) 417 { 418 int error; 419 const char *code; 420 struct apm_power_info pi; 421 422 switch (event_code) { 423 case APM_USER_STANDBY_REQ: 424 DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n")); 425 if (apm_do_standby) { 426 if (apm_op_inprog == 0 && apm_record_event(sc, event_code)) 427 apm_userstandbys++; 428 apm_op_inprog++; 429 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 430 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG); 431 } else { 432 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, 433 APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED); 434 /* in case BIOS hates being spurned */ 435 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1); 436 } 437 break; 438 439 case APM_STANDBY_REQ: 440 DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n")); 441 if (apm_standbys || apm_suspends) { 442 DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM, 443 ("damn fool BIOS did not wait for answer\n")); 444 /* just give up the fight */ 445 apm_damn_fool_bios = 1; 446 } 447 if (apm_do_standby) { 448 if (apm_op_inprog == 0 && 449 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_op_inprog == 0 && 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_op_inprog == 0 && 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, 0, &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, 0, &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 int 634 apm_match(void) 635 { 636 static int got; 637 return !got++; 638 } 639 640 void 641 apm_attach(struct apm_softc *sc) 642 { 643 struct apm_power_info pinfo; 644 u_int numbatts, capflags; 645 int error; 646 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 if (sc->sc_ops->aa_cpu_busy) 681 (*sc->sc_ops->aa_cpu_busy)(sc->sc_cookie); 682 683 lockinit(&sc->sc_lock, PWAIT, "apmlk", 0, 0); 684 685 /* Initial state is `resumed'. */ 686 sc->sc_power_state = PWR_RESUME; 687 688 /* Do an initial check. */ 689 apm_periodic_check(sc); 690 691 /* 692 * Create a kernel thread to periodically check for APM events, 693 * and notify other subsystems when they occur. 694 */ 695 if (kthread_create(PRI_NONE, 0, NULL, apm_thread, sc, 696 &sc->sc_thread, "%s", sc->sc_dev.dv_xname) != 0) { 697 /* 698 * We were unable to create the APM thread; bail out. 699 */ 700 if (sc->sc_ops->aa_disconnect) 701 (*sc->sc_ops->aa_disconnect)(sc->sc_cookie); 702 printf("%s: unable to create thread, " 703 "kernel APM support disabled\n", 704 sc->sc_dev.dv_xname); 705 } 706 } 707 708 void 709 apm_thread(void *arg) 710 { 711 struct apm_softc *apmsc = arg; 712 713 /* 714 * Loop forever, doing a periodic check for APM events. 715 */ 716 for (;;) { 717 APM_LOCK(apmsc); 718 apm_periodic_check(apmsc); 719 APM_UNLOCK(apmsc); 720 (void) tsleep(apmsc, PWAIT, "apmev", (8 * hz) / 7); 721 } 722 } 723 724 int 725 apmopen(dev_t dev, int flag, int mode, struct lwp *l) 726 { 727 int unit = APMUNIT(dev); 728 int ctl = APM(dev); 729 int error = 0; 730 struct apm_softc *sc; 731 732 if (unit >= apm_cd.cd_ndevs) 733 return ENXIO; 734 sc = apm_cd.cd_devs[unit]; 735 if (!sc) 736 return ENXIO; 737 738 if (!apm_inited) 739 return ENXIO; 740 741 DPRINTF(APMDEBUG_DEVICE, 742 ("apmopen: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode)); 743 744 APM_LOCK(sc); 745 switch (ctl) { 746 case APM_CTL: 747 if (!(flag & FWRITE)) { 748 error = EINVAL; 749 break; 750 } 751 if (sc->sc_flags & SCFLAG_OWRITE) { 752 error = EBUSY; 753 break; 754 } 755 sc->sc_flags |= SCFLAG_OWRITE; 756 break; 757 case APM_NORMAL: 758 if (!(flag & FREAD) || (flag & FWRITE)) { 759 error = EINVAL; 760 break; 761 } 762 sc->sc_flags |= SCFLAG_OREAD; 763 break; 764 default: 765 error = ENXIO; 766 break; 767 } 768 APM_UNLOCK(sc); 769 770 return (error); 771 } 772 773 int 774 apmclose(dev_t dev, int flag, int mode, 775 struct lwp *l) 776 { 777 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)]; 778 int ctl = APM(dev); 779 780 DPRINTF(APMDEBUG_DEVICE, 781 ("apmclose: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode)); 782 783 APM_LOCK(sc); 784 switch (ctl) { 785 case APM_CTL: 786 sc->sc_flags &= ~SCFLAG_OWRITE; 787 break; 788 case APM_NORMAL: 789 sc->sc_flags &= ~SCFLAG_OREAD; 790 break; 791 } 792 if ((sc->sc_flags & SCFLAG_OPEN) == 0) { 793 sc->sc_event_count = 0; 794 sc->sc_event_ptr = 0; 795 } 796 APM_UNLOCK(sc); 797 return 0; 798 } 799 800 int 801 apmioctl(dev_t dev, u_long cmd, void *data, int flag, 802 struct lwp *l) 803 { 804 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)]; 805 struct apm_power_info *powerp; 806 struct apm_event_info *evp; 807 #if 0 808 struct apm_ctl *actl; 809 #endif 810 int i, error = 0; 811 int batt_flags; 812 struct apm_ctl *actl; 813 814 APM_LOCK(sc); 815 switch (cmd) { 816 case APM_IOC_STANDBY: 817 if (!apm_do_standby) { 818 error = EOPNOTSUPP; 819 break; 820 } 821 822 if ((flag & FWRITE) == 0) { 823 error = EBADF; 824 break; 825 } 826 apm_userstandbys++; 827 break; 828 829 case APM_IOC_DEV_CTL: 830 actl = (struct apm_ctl *)data; 831 if ((flag & FWRITE) == 0) { 832 error = EBADF; 833 break; 834 } 835 #if 0 836 apm_get_powstate(actl->dev); /* XXX */ 837 #endif 838 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, actl->dev, 839 actl->mode); 840 apm_suspends++; 841 break; 842 843 case APM_IOC_SUSPEND: 844 if ((flag & FWRITE) == 0) { 845 error = EBADF; 846 break; 847 } 848 apm_suspends++; 849 break; 850 851 case APM_IOC_NEXTEVENT: 852 if (!sc->sc_event_count) 853 error = EAGAIN; 854 else { 855 evp = (struct apm_event_info *)data; 856 i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count; 857 i %= APM_NEVENTS; 858 *evp = sc->sc_event_list[i]; 859 sc->sc_event_count--; 860 } 861 break; 862 863 case APM_IOC_GETPOWER: 864 powerp = (struct apm_power_info *)data; 865 if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, 866 powerp)) != 0) { 867 apm_perror("ioctl get power status", error); 868 error = EIO; 869 break; 870 } 871 switch (apm_minver) { 872 case 0: 873 break; 874 case 1: 875 default: 876 batt_flags = powerp->battery_flags; 877 powerp->battery_state = APM_BATT_UNKNOWN; 878 if (batt_flags & APM_BATT_FLAG_HIGH) 879 powerp->battery_state = APM_BATT_HIGH; 880 else if (batt_flags & APM_BATT_FLAG_LOW) 881 powerp->battery_state = APM_BATT_LOW; 882 else if (batt_flags & APM_BATT_FLAG_CRITICAL) 883 powerp->battery_state = APM_BATT_CRITICAL; 884 else if (batt_flags & APM_BATT_FLAG_CHARGING) 885 powerp->battery_state = APM_BATT_CHARGING; 886 else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY) 887 powerp->battery_state = APM_BATT_ABSENT; 888 break; 889 } 890 break; 891 892 default: 893 error = ENOTTY; 894 } 895 APM_UNLOCK(sc); 896 897 return (error); 898 } 899 900 int 901 apmpoll(dev_t dev, int events, struct lwp *l) 902 { 903 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)]; 904 int revents = 0; 905 906 APM_LOCK(sc); 907 if (events & (POLLIN | POLLRDNORM)) { 908 if (sc->sc_event_count) 909 revents |= events & (POLLIN | POLLRDNORM); 910 else 911 selrecord(l, &sc->sc_rsel); 912 } 913 APM_UNLOCK(sc); 914 915 return (revents); 916 } 917 918 static void 919 filt_apmrdetach(struct knote *kn) 920 { 921 struct apm_softc *sc = kn->kn_hook; 922 923 APM_LOCK(sc); 924 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); 925 APM_UNLOCK(sc); 926 } 927 928 static int 929 filt_apmread(struct knote *kn, long hint) 930 { 931 struct apm_softc *sc = kn->kn_hook; 932 933 kn->kn_data = sc->sc_event_count; 934 return (kn->kn_data > 0); 935 } 936 937 static const struct filterops apmread_filtops = 938 { 1, NULL, filt_apmrdetach, filt_apmread }; 939 940 int 941 apmkqfilter(dev_t dev, struct knote *kn) 942 { 943 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)]; 944 struct klist *klist; 945 946 switch (kn->kn_filter) { 947 case EVFILT_READ: 948 klist = &sc->sc_rsel.sel_klist; 949 kn->kn_fop = &apmread_filtops; 950 break; 951 952 default: 953 return (1); 954 } 955 956 kn->kn_hook = sc; 957 958 APM_LOCK(sc); 959 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 960 APM_UNLOCK(sc); 961 962 return (0); 963 } 964