Annotation of sys/arch/i386/i386/apm.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: apm.c,v 1.76 2007/07/02 17:11:29 thib Exp $ */
! 2:
! 3: /*-
! 4: * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
! 5: * Copyright (c) 1995 John T. Kohl. All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by the University of
! 18: * California, Berkeley and its contributors.
! 19: * 4. Neither the name of the University nor the names of its contributors
! 20: * may be used to endorse or promote products derived from this software
! 21: * without specific prior written permission.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 29: * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 33: * SUCH DAMAGE.
! 34: *
! 35: */
! 36:
! 37: #include "apm.h"
! 38:
! 39: #if NAPM > 1
! 40: #error only one APM device may be configured
! 41: #endif
! 42:
! 43: #include <sys/param.h>
! 44: #include <sys/systm.h>
! 45: #include <sys/signalvar.h>
! 46: #include <sys/kernel.h>
! 47: #include <sys/kthread.h>
! 48: #include <sys/rwlock.h>
! 49: #include <sys/proc.h>
! 50: #include <sys/user.h>
! 51: #include <sys/malloc.h>
! 52: #include <sys/device.h>
! 53: #include <sys/fcntl.h>
! 54: #include <sys/ioctl.h>
! 55: #include <sys/event.h>
! 56: #include <sys/mount.h> /* for vfs_syncwait() proto */
! 57:
! 58: #include <machine/conf.h>
! 59: #include <machine/cpu.h>
! 60: #include <machine/cpufunc.h>
! 61: #include <machine/gdt.h>
! 62: #include <machine/psl.h>
! 63:
! 64: #include <dev/isa/isareg.h>
! 65: #include <i386/isa/isa_machdep.h>
! 66: #include <i386/isa/nvram.h>
! 67: #include <dev/isa/isavar.h>
! 68:
! 69: #include <machine/biosvar.h>
! 70: #include <machine/apmvar.h>
! 71:
! 72: #if defined(APMDEBUG)
! 73: #define DPRINTF(x) printf x
! 74: #else
! 75: #define DPRINTF(x) /**/
! 76: #endif
! 77:
! 78: struct cfdriver apm_cd = {
! 79: NULL, "apm", DV_DULL
! 80: };
! 81:
! 82: struct apm_softc {
! 83: struct device sc_dev;
! 84: struct klist sc_note;
! 85: int sc_flags;
! 86: int batt_life;
! 87: struct proc *sc_thread;
! 88: struct rwlock sc_lock;
! 89: };
! 90: #define SCFLAG_OREAD 0x0000001
! 91: #define SCFLAG_OWRITE 0x0000002
! 92: #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
! 93:
! 94: int apmprobe(struct device *, void *, void *);
! 95: void apmattach(struct device *, struct device *, void *);
! 96:
! 97: struct cfattach apm_ca = {
! 98: sizeof(struct apm_softc), apmprobe, apmattach
! 99: };
! 100:
! 101: void filt_apmrdetach(struct knote *kn);
! 102: int filt_apmread(struct knote *kn, long hint);
! 103:
! 104: struct filterops apmread_filtops = {
! 105: 1, NULL, filt_apmrdetach, filt_apmread
! 106: };
! 107:
! 108: /* battery percentage at where we get verbose in our warnings. This
! 109: * value can be changed using sysctl(8), value machdep.apmwarn.
! 110: * Setting it to zero kills all warnings
! 111: */
! 112: int cpu_apmwarn = 10;
! 113:
! 114: #define APM_RESUME_HOLDOFF 3
! 115:
! 116: /*
! 117: * Flags to control kernel display
! 118: * SCFLAG_NOPRINT: do not output APM power messages due to
! 119: * a power change event.
! 120: *
! 121: * SCFLAG_PCTPRINT: do not output APM power messages due to
! 122: * to a power change event unless the battery
! 123: * percentage changes.
! 124: */
! 125: #define SCFLAG_NOPRINT 0x0008000
! 126: #define SCFLAG_PCTPRINT 0x0004000
! 127: #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
! 128:
! 129: #define APMUNIT(dev) (minor(dev)&0xf0)
! 130: #define APMDEV(dev) (minor(dev)&0x0f)
! 131: #define APMDEV_NORMAL 0
! 132: #define APMDEV_CTL 8
! 133:
! 134: int apm_standbys;
! 135: int apm_userstandbys;
! 136: int apm_suspends;
! 137: int apm_resumes;
! 138: int apm_battlow;
! 139: int apm_evindex;
! 140: int apm_error;
! 141: int apm_op_inprog;
! 142:
! 143: u_int apm_flags;
! 144: u_char apm_majver;
! 145: u_char apm_minver;
! 146: int apm_dobusy = 0;
! 147: int apm_doidle = 0;
! 148: int apm_bebatt = 0;
! 149: int apm_idle_called = 0;
! 150: int apm_attached = 0;
! 151:
! 152: struct {
! 153: u_int32_t entry;
! 154: u_int16_t seg;
! 155: u_int16_t pad;
! 156: } apm_ep;
! 157:
! 158: struct apmregs {
! 159: u_int32_t ax;
! 160: u_int32_t bx;
! 161: u_int32_t cx;
! 162: u_int32_t dx;
! 163: };
! 164:
! 165: int apmcall(u_int, u_int, struct apmregs *);
! 166: void apm_power_print(struct apm_softc *, struct apmregs *);
! 167: int apm_handle_event(struct apm_softc *, struct apmregs *);
! 168: void apm_set_ver(struct apm_softc *);
! 169: int apm_periodic_check(struct apm_softc *);
! 170: void apm_thread_create(void *v);
! 171: void apm_thread(void *);
! 172: void apm_disconnect(struct apm_softc *);
! 173: void apm_perror(const char *, struct apmregs *);
! 174: void apm_powmgt_enable(int onoff);
! 175: void apm_powmgt_engage(int onoff, u_int devid);
! 176: /* void apm_devpowmgt_enable(int onoff, u_int devid); */
! 177: int apm_record_event(struct apm_softc *sc, u_int type);
! 178: const char *apm_err_translate(int code);
! 179:
! 180: #define apm_get_powstat(r) apmcall(APM_POWER_STATUS, APM_DEV_ALLDEVS, r)
! 181: void apm_standby(void);
! 182: void apm_suspend(void);
! 183: void apm_resume(struct apm_softc *, struct apmregs *);
! 184:
! 185: static int __inline
! 186: apm_get_event(struct apmregs *r)
! 187: {
! 188: int rv;
! 189:
! 190: bzero(r, sizeof(*r));
! 191: rv = apmcall(APM_GET_PM_EVENT, 0, r);
! 192: return rv;
! 193: }
! 194:
! 195: const char *
! 196: apm_err_translate(int code)
! 197: {
! 198: switch (code) {
! 199: case APM_ERR_PM_DISABLED:
! 200: return "power management disabled";
! 201: case APM_ERR_REALALREADY:
! 202: return "real mode interface already connected";
! 203: case APM_ERR_NOTCONN:
! 204: return "interface not connected";
! 205: case APM_ERR_16ALREADY:
! 206: return "16-bit interface already connected";
! 207: case APM_ERR_16NOTSUPP:
! 208: return "16-bit interface not supported";
! 209: case APM_ERR_32ALREADY:
! 210: return "32-bit interface already connected";
! 211: case APM_ERR_32NOTSUPP:
! 212: return "32-bit interface not supported";
! 213: case APM_ERR_UNRECOG_DEV:
! 214: return "unrecognized device ID";
! 215: case APM_ERR_ERANGE:
! 216: return "parameter out of range";
! 217: case APM_ERR_NOTENGAGED:
! 218: return "interface not engaged";
! 219: case APM_ERR_UNABLE:
! 220: return "unable to enter requested state";
! 221: case APM_ERR_NOEVENTS:
! 222: return "No pending events";
! 223: case APM_ERR_NOT_PRESENT:
! 224: return "No APM present";
! 225: default:
! 226: return "unknown error code?";
! 227: }
! 228: }
! 229:
! 230: int apmerrors = 0;
! 231:
! 232: void
! 233: apm_perror(const char *str, struct apmregs *regs)
! 234: {
! 235: printf("apm0: APM %s: %s (%d)\n", str,
! 236: apm_err_translate(APM_ERR_CODE(regs)),
! 237: APM_ERR_CODE(regs));
! 238: delay(1000000);
! 239:
! 240: apmerrors++;
! 241: }
! 242:
! 243: void
! 244: apm_power_print (struct apm_softc *sc, struct apmregs *regs)
! 245: {
! 246: #if !defined(APM_NOPRINT)
! 247: sc->batt_life = BATT_LIFE(regs);
! 248: if (BATT_LIFE(regs) != APM_BATT_LIFE_UNKNOWN) {
! 249: printf("%s: battery life expectancy %d%%\n",
! 250: sc->sc_dev.dv_xname,
! 251: BATT_LIFE(regs));
! 252: }
! 253: printf("%s: AC ", sc->sc_dev.dv_xname);
! 254: switch (AC_STATE(regs)) {
! 255: case APM_AC_OFF:
! 256: printf("off,");
! 257: break;
! 258: case APM_AC_ON:
! 259: printf("on,");
! 260: break;
! 261: case APM_AC_BACKUP:
! 262: printf("backup power,");
! 263: break;
! 264: default:
! 265: case APM_AC_UNKNOWN:
! 266: printf("unknown,");
! 267: break;
! 268: }
! 269: if (apm_minver == 0) {
! 270: printf(" battery is ");
! 271: switch (BATT_STATE(regs)) {
! 272: case APM_BATT_HIGH:
! 273: printf("high");
! 274: break;
! 275: case APM_BATT_LOW:
! 276: printf("low");
! 277: break;
! 278: case APM_BATT_CRITICAL:
! 279: printf("CRITICAL");
! 280: break;
! 281: case APM_BATT_CHARGING:
! 282: printf("charging");
! 283: break;
! 284: case APM_BATT_UNKNOWN:
! 285: printf("unknown");
! 286: break;
! 287: default:
! 288: printf("undecoded (%x)", BATT_STATE(regs));
! 289: break;
! 290: }
! 291: } else if (apm_minver >= 1) {
! 292: if (BATT_FLAGS(regs) & APM_BATT_FLAG_NOBATTERY)
! 293: printf(" no battery");
! 294: else {
! 295: printf(" battery charge ");
! 296: if (BATT_FLAGS(regs) & APM_BATT_FLAG_HIGH)
! 297: printf("high");
! 298: else if (BATT_FLAGS(regs) & APM_BATT_FLAG_LOW)
! 299: printf("low");
! 300: else if (BATT_FLAGS(regs) & APM_BATT_FLAG_CRITICAL)
! 301: printf("critical");
! 302: else
! 303: printf("unknown");
! 304: if (BATT_FLAGS(regs) & APM_BATT_FLAG_CHARGING)
! 305: printf(", charging");
! 306: if (BATT_REM_VALID(regs)) {
! 307: int life = BATT_REMAINING(regs);
! 308: if (apm_bebatt)
! 309: life = swap16(life);
! 310: printf(", estimated %d:%02d hours",
! 311: life / 60, life % 60);
! 312: }
! 313: }
! 314: }
! 315:
! 316: printf("\n");
! 317: #endif
! 318: }
! 319:
! 320: void
! 321: apm_suspend()
! 322: {
! 323: dopowerhooks(PWR_SUSPEND);
! 324:
! 325: if (cold)
! 326: vfs_syncwait(0);
! 327:
! 328: (void)apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_SUSPEND);
! 329: }
! 330:
! 331: void
! 332: apm_standby()
! 333: {
! 334: dopowerhooks(PWR_STANDBY);
! 335:
! 336: if (cold)
! 337: vfs_syncwait(0);
! 338:
! 339: (void)apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_STANDBY);
! 340: }
! 341:
! 342: void
! 343: apm_resume(struct apm_softc *sc, struct apmregs *regs)
! 344: {
! 345: extern int perflevel;
! 346:
! 347: apm_resumes = APM_RESUME_HOLDOFF;
! 348:
! 349: /* they say that some machines may require reinitializing the clock */
! 350: initrtclock();
! 351:
! 352: inittodr(time_second);
! 353: /* lower bit in cx means pccard was powered down */
! 354: dopowerhooks(PWR_RESUME);
! 355: apm_record_event(sc, regs->bx);
! 356:
! 357: /* acknowledge any rtc interrupt we may have missed */
! 358: rtcdrain(NULL);
! 359:
! 360: /* restore hw.setperf */
! 361: if (cpu_setperf != NULL)
! 362: cpu_setperf(perflevel);
! 363: }
! 364:
! 365: int
! 366: apm_record_event(struct apm_softc *sc, u_int type)
! 367: {
! 368: if (!apm_error && (sc->sc_flags & SCFLAG_OPEN) == 0) {
! 369: DPRINTF(("apm_record_event: no user waiting\n"));
! 370: apm_error++;
! 371: return 1;
! 372: }
! 373:
! 374: apm_evindex++;
! 375: KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(type, apm_evindex));
! 376: return (0);
! 377: }
! 378:
! 379: int
! 380: apm_handle_event(struct apm_softc *sc, struct apmregs *regs)
! 381: {
! 382: struct apmregs nregs;
! 383: int ret = 0;
! 384:
! 385: switch (regs->bx) {
! 386: case APM_NOEVENT:
! 387: ret++;
! 388: break;
! 389:
! 390: case APM_USER_STANDBY_REQ:
! 391: if (apm_resumes || apm_op_inprog)
! 392: break;
! 393: DPRINTF(("user wants STANDBY--fat chance\n"));
! 394: apm_op_inprog++;
! 395: if (apm_record_event(sc, regs->bx)) {
! 396: DPRINTF(("standby ourselves\n"));
! 397: apm_userstandbys++;
! 398: }
! 399: break;
! 400: case APM_STANDBY_REQ:
! 401: if (apm_resumes || apm_op_inprog)
! 402: break;
! 403: DPRINTF(("standby requested\n"));
! 404: if (apm_standbys || apm_suspends) {
! 405: DPRINTF(("premature standby\n"));
! 406: apm_error++;
! 407: ret++;
! 408: }
! 409: apm_op_inprog++;
! 410: if (apm_record_event(sc, regs->bx)) {
! 411: DPRINTF(("standby ourselves\n"));
! 412: apm_standbys++;
! 413: }
! 414: break;
! 415: case APM_USER_SUSPEND_REQ:
! 416: if (apm_resumes || apm_op_inprog)
! 417: break;
! 418: DPRINTF(("user wants suspend--fat chance!\n"));
! 419: apm_op_inprog++;
! 420: if (apm_record_event(sc, regs->bx)) {
! 421: DPRINTF(("suspend ourselves\n"));
! 422: apm_suspends++;
! 423: }
! 424: break;
! 425: case APM_SUSPEND_REQ:
! 426: if (apm_resumes || apm_op_inprog)
! 427: break;
! 428: DPRINTF(("suspend requested\n"));
! 429: if (apm_standbys || apm_suspends) {
! 430: DPRINTF(("premature suspend\n"));
! 431: apm_error++;
! 432: ret++;
! 433: }
! 434: apm_op_inprog++;
! 435: if (apm_record_event(sc, regs->bx)) {
! 436: DPRINTF(("suspend ourselves\n"));
! 437: apm_suspends++;
! 438: }
! 439: break;
! 440: case APM_POWER_CHANGE:
! 441: DPRINTF(("power status change\n"));
! 442: if (apm_get_powstat(&nregs) == 0 &&
! 443: BATT_LIFE(&nregs) != APM_BATT_LIFE_UNKNOWN &&
! 444: BATT_LIFE(&nregs) < cpu_apmwarn &&
! 445: (sc->sc_flags & SCFLAG_PRINT) != SCFLAG_NOPRINT &&
! 446: ((sc->sc_flags & SCFLAG_PRINT) != SCFLAG_PCTPRINT ||
! 447: sc->batt_life != BATT_LIFE(&nregs)))
! 448: apm_power_print(sc, &nregs);
! 449: apm_record_event(sc, regs->bx);
! 450: break;
! 451: case APM_NORMAL_RESUME:
! 452: DPRINTF(("system resumed\n"));
! 453: apm_resume(sc, regs);
! 454: break;
! 455: case APM_CRIT_RESUME:
! 456: DPRINTF(("system resumed without us!\n"));
! 457: apm_resume(sc, regs);
! 458: break;
! 459: case APM_SYS_STANDBY_RESUME:
! 460: DPRINTF(("system standby resume\n"));
! 461: apm_resume(sc, regs);
! 462: break;
! 463: case APM_UPDATE_TIME:
! 464: DPRINTF(("update time, please\n"));
! 465: inittodr(time_second);
! 466: apm_record_event(sc, regs->bx);
! 467: break;
! 468: case APM_CRIT_SUSPEND_REQ:
! 469: DPRINTF(("suspend required immediately\n"));
! 470: apm_record_event(sc, regs->bx);
! 471: apm_suspend();
! 472: break;
! 473: case APM_BATTERY_LOW:
! 474: DPRINTF(("Battery low!\n"));
! 475: apm_battlow++;
! 476: apm_record_event(sc, regs->bx);
! 477: break;
! 478: case APM_CAPABILITY_CHANGE:
! 479: DPRINTF(("capability change\n"));
! 480: if (apm_minver < 2) {
! 481: DPRINTF(("adult event\n"));
! 482: } else {
! 483: if (apmcall(APM_GET_CAPABILITIES, APM_DEV_APM_BIOS,
! 484: &nregs) != 0) {
! 485: apm_perror("get capabilities", &nregs);
! 486: } else {
! 487: apm_get_powstat(&nregs);
! 488: }
! 489: }
! 490: break;
! 491: default: {
! 492: #ifdef APMDEBUG
! 493: char *p;
! 494: switch (regs->bx >> 8) {
! 495: case 0: p = "reserved system"; break;
! 496: case 1: p = "reserved device"; break;
! 497: case 2: p = "OEM defined"; break;
! 498: default:p = "reserved"; break;
! 499: }
! 500: #endif
! 501: DPRINTF(("apm_handle_event: %s event, code %d\n", p, regs->bx));
! 502: }
! 503: }
! 504: return ret;
! 505: }
! 506:
! 507: int
! 508: apm_periodic_check(struct apm_softc *sc)
! 509: {
! 510: struct apmregs regs;
! 511: int ret = 0;
! 512:
! 513: if (apm_op_inprog)
! 514: apm_set_powstate(APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
! 515:
! 516: while (1) {
! 517: if (apm_get_event(®s) != 0) {
! 518: /* i think some bioses combine the error codes */
! 519: if (!(APM_ERR_CODE(®s) & APM_ERR_NOEVENTS))
! 520: apm_perror("get event", ®s);
! 521: break;
! 522: }
! 523:
! 524: if (apm_handle_event(sc, ®s))
! 525: break;
! 526: }
! 527:
! 528: if (apm_error || APM_ERR_CODE(®s) == APM_ERR_NOTCONN)
! 529: ret = -1;
! 530:
! 531: if (apm_suspends /*|| (apm_battlow && apm_userstandbys)*/) {
! 532: apm_op_inprog = 0;
! 533: apm_suspend();
! 534: } else if (apm_standbys || apm_userstandbys) {
! 535: apm_op_inprog = 0;
! 536: apm_standby();
! 537: }
! 538: apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
! 539: apm_error = 0;
! 540:
! 541: if (apm_resumes)
! 542: apm_resumes--;
! 543: return (ret);
! 544: }
! 545:
! 546: void
! 547: apm_powmgt_enable(int onoff)
! 548: {
! 549: struct apmregs regs;
! 550:
! 551: bzero(®s, sizeof(regs));
! 552: regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
! 553: if (apmcall(APM_PWR_MGT_ENABLE,
! 554: (apm_minver? APM_DEV_APM_BIOS : APM_MGT_ALL), ®s) != 0)
! 555: apm_perror("power management enable", ®s);
! 556: }
! 557:
! 558: void
! 559: apm_powmgt_engage(int onoff, u_int dev)
! 560: {
! 561: struct apmregs regs;
! 562:
! 563: if (apm_minver == 0)
! 564: return;
! 565: bzero(®s, sizeof(regs));
! 566: regs.cx = onoff ? APM_MGT_ENGAGE : APM_MGT_DISENGAGE;
! 567: if (apmcall(APM_PWR_MGT_ENGAGE, dev, ®s) != 0)
! 568: printf("apm0: APM engage (device %x): %s (%d)\n",
! 569: dev, apm_err_translate(APM_ERR_CODE(®s)),
! 570: APM_ERR_CODE(®s));
! 571: }
! 572:
! 573: #ifdef notused
! 574: void
! 575: apm_devpowmgt_enable(int onoff, u_int dev)
! 576: {
! 577: struct apmregs regs;
! 578:
! 579: if (apm_minver == 0)
! 580: return;
! 581: /* enable is auto BIOS management.
! 582: * disable is program control.
! 583: */
! 584: bzero(®s, sizeof(regs));
! 585: regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
! 586: if (apmcall(APM_DEVICE_MGMT_ENABLE, dev, ®s) != 0)
! 587: printf("APM device engage (device %x): %s (%d)\n",
! 588: dev, apm_err_translate(APM_ERR_CODE(®s)),
! 589: APM_ERR_CODE(®s));
! 590: }
! 591: #endif
! 592:
! 593: int
! 594: apm_set_powstate(u_int dev, u_int state)
! 595: {
! 596: struct apmregs regs;
! 597:
! 598: if (!apm_cd.cd_ndevs || (apm_minver == 0 && state > APM_SYS_OFF))
! 599: return EINVAL;
! 600: bzero(®s, sizeof(regs));
! 601: regs.cx = state;
! 602: if (apmcall(APM_SET_PWR_STATE, dev, ®s) != 0) {
! 603: apm_perror("set power state", ®s);
! 604: if (APM_ERR_CODE(®s) == APM_ERR_UNRECOG_DEV)
! 605: return ENXIO;
! 606: else
! 607: return EIO;
! 608: }
! 609: return 0;
! 610: }
! 611:
! 612: void
! 613: apm_cpu_busy(void)
! 614: {
! 615: struct apmregs regs;
! 616:
! 617: if (!apm_cd.cd_ndevs) /* No APM device, punt */
! 618: return;
! 619: if (!apm_dobusy)
! 620: return;
! 621: if (!apm_idle_called)
! 622: return;
! 623:
! 624: if (apm_flags & APM_IDLE_SLOWS) {
! 625: bzero(®s, sizeof(regs));
! 626: if (apmcall(APM_CPU_BUSY, 0, ®s) != 0) {
! 627: #ifdef DIAGNOSTIC
! 628: apm_perror("set CPU busy", ®s);
! 629: #endif
! 630: }
! 631: apm_idle_called = 0;
! 632: }
! 633: }
! 634:
! 635: void
! 636: apm_cpu_idle(void)
! 637: {
! 638: struct apmregs regs;
! 639: static u_int64_t call_apm = 0;
! 640:
! 641: if (!apm_cd.cd_ndevs) { /* No APM device, wait for next interrupt */
! 642: __asm __volatile("sti;hlt");
! 643: return;
! 644: }
! 645:
! 646: if (!apm_doidle) {
! 647: __asm __volatile("sti;hlt");
! 648: return;
! 649: }
! 650:
! 651: /*
! 652: * We call the bios APM_IDLE routine here only when we
! 653: * have been idle for some time - otherwise we just hlt.
! 654: */
! 655:
! 656: if (call_apm != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) {
! 657: /* Always call BIOS halt/idle stuff */
! 658: bzero(®s, sizeof(regs));
! 659: if (apmcall(APM_CPU_IDLE, 0, ®s) != 0) {
! 660: #ifdef APMDEBUG
! 661: apm_perror("set CPU idle", ®s);
! 662: #endif
! 663: }
! 664: apm_idle_called = 1;
! 665: /* If BIOS did halt, don't do it again! */
! 666: if (apm_flags & APM_IDLE_SLOWS) {
! 667: __asm __volatile("sti;hlt");
! 668: }
! 669: call_apm = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE];
! 670: } else {
! 671: __asm __volatile("sti;hlt");
! 672: }
! 673: }
! 674:
! 675: void
! 676: apm_set_ver(struct apm_softc *self)
! 677: {
! 678: struct apmregs regs;
! 679: int rv = 0;
! 680:
! 681: bzero(®s, sizeof(regs));
! 682: regs.cx = APM_VERSION;
! 683:
! 684: if (APM_MAJOR(apm_flags) == 1 && APM_MINOR(apm_flags) == 2 &&
! 685: (rv = apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, ®s)) == 0) {
! 686: apm_majver = APM_CONN_MAJOR(®s);
! 687: apm_minver = APM_CONN_MINOR(®s);
! 688: } else {
! 689: #ifdef APMDEBUG
! 690: if (rv)
! 691: apm_perror("set version 1.2", ®s);
! 692: #endif
! 693: /* try downgrading to 1.1 */
! 694: bzero(®s, sizeof(regs));
! 695: regs.cx = 0x0101;
! 696:
! 697: if (apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, ®s) == 0) {
! 698: apm_majver = 1;
! 699: apm_minver = 1;
! 700: } else {
! 701: #ifdef APMDEBUG
! 702: apm_perror("set version 1.1", ®s);
! 703: #endif
! 704: /* stay w/ flags then */
! 705: apm_majver = APM_MAJOR(apm_flags);
! 706: apm_minver = APM_MINOR(apm_flags);
! 707:
! 708: /* fix version for some endianess-challenged compaqs */
! 709: if (!apm_majver) {
! 710: apm_majver = 1;
! 711: apm_minver = 0;
! 712: }
! 713: }
! 714: }
! 715: printf(": Power Management spec V%d.%d", apm_majver, apm_minver);
! 716: if (apm_flags & APM_IDLE_SLOWS) {
! 717: DPRINTF((" (slowidle)"));
! 718: apm_dobusy = 1;
! 719: apm_doidle = 1;
! 720: } else {
! 721: apm_dobusy = 0;
! 722: apm_doidle = 1;
! 723: }
! 724: #ifdef DIAGNOSTIC
! 725: if (apm_flags & APM_BIOS_PM_DISABLED)
! 726: printf(" (BIOS mgmt disabled)");
! 727: if (apm_flags & APM_BIOS_PM_DISENGAGED)
! 728: printf(" (BIOS managing devices)");
! 729: #endif
! 730: printf("\n");
! 731: }
! 732:
! 733: void
! 734: apm_disconnect(struct apm_softc *sc)
! 735: {
! 736: struct apmregs regs;
! 737:
! 738: bzero(®s, sizeof(regs));
! 739: if (apmcall(APM_SYSTEM_DEFAULTS,
! 740: (apm_minver == 1 ? APM_DEV_ALLDEVS : APM_DEFAULTS_ALL), ®s))
! 741: apm_perror("system defaults failed", ®s);
! 742:
! 743: if (apmcall(APM_DISCONNECT, APM_DEV_APM_BIOS, ®s))
! 744: apm_perror("disconnect failed", ®s);
! 745: else
! 746: printf("%s: disconnected\n", sc->sc_dev.dv_xname);
! 747: apm_flags |= APM_BIOS_PM_DISABLED;
! 748: }
! 749:
! 750: int
! 751: apmprobe(struct device *parent, void *match, void *aux)
! 752: {
! 753: struct bios_attach_args *ba = aux;
! 754: bios_apminfo_t *ap = ba->bios_apmp;
! 755: bus_space_handle_t ch, dh;
! 756:
! 757: if (apm_cd.cd_ndevs || strcmp(ba->bios_dev, "apm") ||
! 758: !(ba->bios_apmp->apm_detail & APM_32BIT_SUPPORTED)) {
! 759: DPRINTF(("%s: %x\n", ba->bios_dev, ba->bios_apmp->apm_detail));
! 760: return 0;
! 761: }
! 762:
! 763: /* addresses check
! 764: since pc* console and vga* probes much later
! 765: we cannot check for video memory being mapped
! 766: for apm stuff w/ bus_space_map() */
! 767: if (ap->apm_code_len == 0 ||
! 768: (ap->apm_code32_base < IOM_BEGIN &&
! 769: ap->apm_code32_base + ap->apm_code_len > IOM_BEGIN) ||
! 770: (ap->apm_code16_base < IOM_BEGIN &&
! 771: ap->apm_code16_base + ap->apm_code16_len > IOM_BEGIN) ||
! 772: (ap->apm_data_base < IOM_BEGIN &&
! 773: ap->apm_data_base + ap->apm_data_len > IOM_BEGIN))
! 774: return 0;
! 775:
! 776: if (bus_space_map(ba->bios_memt, ap->apm_code32_base,
! 777: ap->apm_code_len, 1, &ch) != 0) {
! 778: DPRINTF(("apm0: can't map code\n"));
! 779: return 0;
! 780: }
! 781: bus_space_unmap(ba->bios_memt, ch, ap->apm_code_len);
! 782:
! 783: if (bus_space_map(ba->bios_memt, ap->apm_data_base,
! 784: ap->apm_data_len, 1, &dh) != 0) {
! 785: DPRINTF(("apm0: can't map data\n"));
! 786: return 0;
! 787: }
! 788: bus_space_unmap(ba->bios_memt, dh, ap->apm_data_len);
! 789: return 1;
! 790: }
! 791:
! 792: void
! 793: apmattach(struct device *parent, struct device *self, void *aux)
! 794: {
! 795: struct bios_attach_args *ba = aux;
! 796: bios_apminfo_t *ap = ba->bios_apmp;
! 797: struct apm_softc *sc = (void *)self;
! 798: struct apmregs regs;
! 799: u_int cbase, clen, l;
! 800: bus_space_handle_t ch16, ch32, dh;
! 801:
! 802: apm_flags = ap->apm_detail;
! 803: /*
! 804: * set up GDT descriptors for APM
! 805: */
! 806: if (apm_flags & APM_32BIT_SUPPORTED) {
! 807:
! 808: /* truncate segments' limits to a page */
! 809: ap->apm_code_len -= (ap->apm_code32_base +
! 810: ap->apm_code_len + 1) & 0xfff;
! 811: ap->apm_code16_len -= (ap->apm_code16_base +
! 812: ap->apm_code16_len + 1) & 0xfff;
! 813: ap->apm_data_len -= (ap->apm_data_base +
! 814: ap->apm_data_len + 1) & 0xfff;
! 815:
! 816: /* adjust version */
! 817: if ((sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK) &&
! 818: (apm_flags & APM_VERMASK) !=
! 819: (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK))
! 820: apm_flags = (apm_flags & ~APM_VERMASK) |
! 821: (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK);
! 822: if (sc->sc_dev.dv_cfdata->cf_flags & APM_NOCLI) {
! 823: extern int apm_cli; /* from apmcall.S */
! 824: apm_cli = 0;
! 825: }
! 826: if (sc->sc_dev.dv_cfdata->cf_flags & APM_BEBATT)
! 827: apm_bebatt = 1;
! 828: apm_ep.seg = GSEL(GAPM32CODE_SEL,SEL_KPL);
! 829: apm_ep.entry = ap->apm_entry;
! 830: cbase = min(ap->apm_code32_base, ap->apm_code16_base);
! 831: clen = max(ap->apm_code32_base + ap->apm_code_len,
! 832: ap->apm_code16_base + ap->apm_code16_len) - cbase;
! 833: if ((cbase <= ap->apm_data_base &&
! 834: cbase + clen >= ap->apm_data_base) ||
! 835: (ap->apm_data_base <= cbase &&
! 836: ap->apm_data_base + ap->apm_data_len >= cbase)) {
! 837: l = max(ap->apm_data_base + ap->apm_data_len + 1,
! 838: cbase + clen + 1) -
! 839: min(ap->apm_data_base, cbase);
! 840: bus_space_map(ba->bios_memt,
! 841: min(ap->apm_data_base, cbase),
! 842: l, 1, &dh);
! 843: ch16 = dh;
! 844: if (ap->apm_data_base < cbase)
! 845: ch16 += cbase - ap->apm_data_base;
! 846: else
! 847: dh += ap->apm_data_base - cbase;
! 848: } else {
! 849:
! 850: bus_space_map(ba->bios_memt, cbase, clen + 1, 1, &ch16);
! 851: bus_space_map(ba->bios_memt, ap->apm_data_base,
! 852: ap->apm_data_len + 1, 1, &dh);
! 853: }
! 854: ch32 = ch16;
! 855: if (ap->apm_code16_base == cbase)
! 856: ch32 += ap->apm_code32_base - cbase;
! 857: else
! 858: ch16 += ap->apm_code16_base - cbase;
! 859:
! 860: setgdt(GAPM32CODE_SEL, (void *)ch32, ap->apm_code_len,
! 861: SDT_MEMERA, SEL_KPL, 1, 0);
! 862: setgdt(GAPM16CODE_SEL, (void *)ch16, ap->apm_code16_len,
! 863: SDT_MEMERA, SEL_KPL, 0, 0);
! 864: setgdt(GAPMDATA_SEL, (void *)dh, ap->apm_data_len, SDT_MEMRWA,
! 865: SEL_KPL, 1, 0);
! 866: DPRINTF((": flags %x code 32:%x/%x[%x] 16:%x/%x[%x] "
! 867: "data %x/%x/%x ep %x (%x:%x)\n%s", apm_flags,
! 868: ap->apm_code32_base, ch32, ap->apm_code_len,
! 869: ap->apm_code16_base, ch16, ap->apm_code16_len,
! 870: ap->apm_data_base, dh, ap->apm_data_len,
! 871: ap->apm_entry, apm_ep.seg, ap->apm_entry+ch32,
! 872: sc->sc_dev.dv_xname));
! 873:
! 874: apm_set_ver(sc);
! 875:
! 876: if (apm_flags & APM_BIOS_PM_DISABLED)
! 877: apm_powmgt_enable(1);
! 878: /*
! 879: * Engage cooperative power mgt (we get to do it)
! 880: * on all devices (v1.1).
! 881: */
! 882: apm_powmgt_engage(1, APM_DEV_ALLDEVS);
! 883:
! 884: bzero(®s, sizeof(regs));
! 885: if (apm_get_powstat(®s) == 0)
! 886: apm_power_print(sc, ®s);
! 887: else
! 888: apm_perror("get power status", ®s);
! 889: apm_cpu_busy();
! 890:
! 891: rw_init(&sc->sc_lock, "apmlk");
! 892:
! 893: /*
! 894: * Do a check once, ignoring any errors. This avoids
! 895: * gratuitous APM disconnects on laptops where the first
! 896: * event in the queue (after a boot) is non-recognizable.
! 897: * The IBM ThinkPad 770Z is one of those.
! 898: */
! 899: apm_periodic_check(sc);
! 900:
! 901: if (apm_periodic_check(sc) == -1) {
! 902: apm_disconnect(sc);
! 903: apm_dobusy = apm_doidle = 0;
! 904: } else {
! 905: kthread_create_deferred(apm_thread_create, sc);
! 906: apm_attached = 1;
! 907: }
! 908: } else {
! 909: setgdt(GAPM32CODE_SEL, NULL, 0, 0, 0, 0, 0);
! 910: setgdt(GAPM16CODE_SEL, NULL, 0, 0, 0, 0, 0);
! 911: setgdt(GAPMDATA_SEL, NULL, 0, 0, 0, 0, 0);
! 912: }
! 913: /* XXX - To go away */
! 914: printf("apm0: flags %x dobusy %d doidle %d\n",
! 915: apm_flags, apm_dobusy, apm_doidle);
! 916: }
! 917:
! 918: void
! 919: apm_thread_create(void *v)
! 920: {
! 921: struct apm_softc *sc = v;
! 922:
! 923: #ifdef MULTIPROCESSOR
! 924: if (ncpus > 1) {
! 925: apm_disconnect(sc);
! 926: apm_dobusy = apm_doidle = 0;
! 927: return;
! 928: }
! 929: #endif
! 930:
! 931: if (kthread_create(apm_thread, sc, &sc->sc_thread, "%s",
! 932: sc->sc_dev.dv_xname)) {
! 933: apm_disconnect(sc);
! 934: printf("%s: failed to create kernel thread, disabled",
! 935: sc->sc_dev.dv_xname);
! 936: apm_dobusy = apm_doidle = 0;
! 937: }
! 938: }
! 939:
! 940: void
! 941: apm_thread(void *v)
! 942: {
! 943: struct apm_softc *sc = v;
! 944:
! 945: for (;;) {
! 946: rw_enter_write(&sc->sc_lock);
! 947: (void) apm_periodic_check(sc);
! 948: rw_exit_write(&sc->sc_lock);
! 949: tsleep(&lbolt, PWAIT, "apmev", 0);
! 950: }
! 951: }
! 952:
! 953: int
! 954: apmopen(dev_t dev, int flag, int mode, struct proc *p)
! 955: {
! 956: struct apm_softc *sc;
! 957: int error = 0;
! 958:
! 959: /* apm0 only */
! 960: if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
! 961: !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
! 962: return ENXIO;
! 963:
! 964: if (apm_flags & APM_BIOS_PM_DISABLED)
! 965: return ENXIO;
! 966:
! 967: DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
! 968: APMDEV(dev), p->p_pid, flag, mode));
! 969:
! 970: rw_enter_write(&sc->sc_lock);
! 971: switch (APMDEV(dev)) {
! 972: case APMDEV_CTL:
! 973: if (!(flag & FWRITE)) {
! 974: error = EINVAL;
! 975: break;
! 976: }
! 977: if (sc->sc_flags & SCFLAG_OWRITE) {
! 978: error = EBUSY;
! 979: break;
! 980: }
! 981: sc->sc_flags |= SCFLAG_OWRITE;
! 982: break;
! 983: case APMDEV_NORMAL:
! 984: if (!(flag & FREAD) || (flag & FWRITE)) {
! 985: error = EINVAL;
! 986: break;
! 987: }
! 988: sc->sc_flags |= SCFLAG_OREAD;
! 989: break;
! 990: default:
! 991: error = ENXIO;
! 992: break;
! 993: }
! 994: rw_exit_write(&sc->sc_lock);
! 995: return error;
! 996: }
! 997:
! 998: int
! 999: apmclose(dev_t dev, int flag, int mode, struct proc *p)
! 1000: {
! 1001: struct apm_softc *sc;
! 1002:
! 1003: /* apm0 only */
! 1004: if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
! 1005: !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
! 1006: return ENXIO;
! 1007:
! 1008: DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
! 1009:
! 1010: rw_enter_write(&sc->sc_lock);
! 1011: switch (APMDEV(dev)) {
! 1012: case APMDEV_CTL:
! 1013: sc->sc_flags &= ~SCFLAG_OWRITE;
! 1014: break;
! 1015: case APMDEV_NORMAL:
! 1016: sc->sc_flags &= ~SCFLAG_OREAD;
! 1017: break;
! 1018: }
! 1019: rw_exit_write(&sc->sc_lock);
! 1020: return 0;
! 1021: }
! 1022:
! 1023: int
! 1024: apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
! 1025: {
! 1026: struct apm_softc *sc;
! 1027: struct apmregs regs;
! 1028: int error = 0;
! 1029:
! 1030: /* apm0 only */
! 1031: if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
! 1032: !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
! 1033: return ENXIO;
! 1034:
! 1035: rw_enter_write(&sc->sc_lock);
! 1036: switch (cmd) {
! 1037: /* some ioctl names from linux */
! 1038: case APM_IOC_STANDBY:
! 1039: if ((flag & FWRITE) == 0)
! 1040: error = EBADF;
! 1041: else
! 1042: apm_userstandbys++;
! 1043: break;
! 1044: case APM_IOC_SUSPEND:
! 1045: if ((flag & FWRITE) == 0)
! 1046: error = EBADF;
! 1047: else
! 1048: apm_suspends++;
! 1049: break;
! 1050: case APM_IOC_PRN_CTL:
! 1051: if ((flag & FWRITE) == 0)
! 1052: error = EBADF;
! 1053: else {
! 1054: int flag = *(int *)data;
! 1055: DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
! 1056: switch (flag) {
! 1057: case APM_PRINT_ON: /* enable printing */
! 1058: sc->sc_flags &= ~SCFLAG_PRINT;
! 1059: break;
! 1060: case APM_PRINT_OFF: /* disable printing */
! 1061: sc->sc_flags &= ~SCFLAG_PRINT;
! 1062: sc->sc_flags |= SCFLAG_NOPRINT;
! 1063: break;
! 1064: case APM_PRINT_PCT: /* disable some printing */
! 1065: sc->sc_flags &= ~SCFLAG_PRINT;
! 1066: sc->sc_flags |= SCFLAG_PCTPRINT;
! 1067: break;
! 1068: default:
! 1069: error = EINVAL;
! 1070: break;
! 1071: }
! 1072: }
! 1073: break;
! 1074: case APM_IOC_DEV_CTL:
! 1075: if ((flag & FWRITE) == 0)
! 1076: error = EBADF;
! 1077: else {
! 1078: struct apm_ctl *actl = (struct apm_ctl *)data;
! 1079:
! 1080: bzero(®s, sizeof(regs));
! 1081: if (!apmcall(APM_GET_POWER_STATE, actl->dev, ®s))
! 1082: printf("%s: dev %04x state %04x\n",
! 1083: sc->sc_dev.dv_xname, dev, regs.cx);
! 1084:
! 1085: error = apm_set_powstate(actl->dev, actl->mode);
! 1086: }
! 1087: break;
! 1088: case APM_IOC_GETPOWER:
! 1089: if (apm_get_powstat(®s) == 0) {
! 1090: struct apm_power_info *powerp =
! 1091: (struct apm_power_info *)data;
! 1092:
! 1093: bzero(powerp, sizeof(*powerp));
! 1094: if (BATT_LIFE(®s) != APM_BATT_LIFE_UNKNOWN)
! 1095: powerp->battery_life = BATT_LIFE(®s);
! 1096: powerp->ac_state = AC_STATE(®s);
! 1097: switch (apm_minver) {
! 1098: case 0:
! 1099: if (!(BATT_FLAGS(®s) & APM_BATT_FLAG_NOBATTERY))
! 1100: powerp->battery_state = BATT_STATE(®s);
! 1101: break;
! 1102: case 1:
! 1103: default:
! 1104: if (BATT_FLAGS(®s) & APM_BATT_FLAG_HIGH)
! 1105: powerp->battery_state = APM_BATT_HIGH;
! 1106: else if (BATT_FLAGS(®s) & APM_BATT_FLAG_LOW)
! 1107: powerp->battery_state = APM_BATT_LOW;
! 1108: else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CRITICAL)
! 1109: powerp->battery_state = APM_BATT_CRITICAL;
! 1110: else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CHARGING)
! 1111: powerp->battery_state = APM_BATT_CHARGING;
! 1112: else if (BATT_FLAGS(®s) & APM_BATT_FLAG_NOBATTERY)
! 1113: powerp->battery_state = APM_BATTERY_ABSENT;
! 1114: else
! 1115: powerp->battery_state = APM_BATT_UNKNOWN;
! 1116: if (BATT_REM_VALID(®s)) {
! 1117: powerp->minutes_left = BATT_REMAINING(®s);
! 1118: if (apm_bebatt)
! 1119: powerp->minutes_left =
! 1120: swap16(powerp->minutes_left);
! 1121: }
! 1122: }
! 1123: } else {
! 1124: apm_perror("ioctl get power status", ®s);
! 1125: error = EIO;
! 1126: }
! 1127: break;
! 1128:
! 1129: default:
! 1130: error = ENOTTY;
! 1131: }
! 1132:
! 1133: rw_exit_write(&sc->sc_lock);
! 1134: return error;
! 1135: }
! 1136:
! 1137: void
! 1138: filt_apmrdetach(struct knote *kn)
! 1139: {
! 1140: struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
! 1141:
! 1142: rw_enter_write(&sc->sc_lock);
! 1143: SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
! 1144: rw_exit_write(&sc->sc_lock);
! 1145: }
! 1146:
! 1147: int
! 1148: filt_apmread(struct knote *kn, long hint)
! 1149: {
! 1150: /* XXX weird kqueue_scan() semantics */
! 1151: if (hint && !kn->kn_data)
! 1152: kn->kn_data = (int)hint;
! 1153: return (1);
! 1154: }
! 1155:
! 1156: int
! 1157: apmkqfilter(dev_t dev, struct knote *kn)
! 1158: {
! 1159: struct apm_softc *sc;
! 1160:
! 1161: /* apm0 only */
! 1162: if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
! 1163: !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
! 1164: return ENXIO;
! 1165:
! 1166: switch (kn->kn_filter) {
! 1167: case EVFILT_READ:
! 1168: kn->kn_fop = &apmread_filtops;
! 1169: break;
! 1170: default:
! 1171: return (1);
! 1172: }
! 1173:
! 1174: kn->kn_hook = (caddr_t)sc;
! 1175:
! 1176: rw_enter_write(&sc->sc_lock);
! 1177: SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);
! 1178: rw_exit_write(&sc->sc_lock);
! 1179: return (0);
! 1180: }
CVSweb