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