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