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