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