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