Annotation of sys/arch/i386/i386/apm.c, Revision 1.1.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