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