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