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