apm.c revision 1.20 1 /* $NetBSD: apm.c,v 1.20 2008/03/12 18:02:21 dyoung 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.20 2008/03/12 18:02:21 dyoung 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 aprint_normal_dev(sc->sc_dev,
238 "battery life expectancy: %d%%\n",
239 pi->battery_life);
240 }
241 aprint_normal_dev(sc->sc_dev, "A/C state: ");
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 aprint_normal_dev(sc->sc_dev, "battery charge state:");
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 aprint_normal_dev(sc->sc_dev, "estimated ");
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 aprint_debug_dev(sc->sc_dev,
312 "apm_suspend: already suspended?\n");
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(PMF_F_NONE);
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 aprint_debug_dev(sc->sc_dev,
338 "apm_standby: already standing by?\n");
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(PMF_F_NONE);
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 aprint_debug_dev(sc->sc_dev, "apm_resume: already running?\n");
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(PMF_F_NONE);
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, 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 selinit(&sc->sc_rsel);
669 selinit(&sc->sc_xsel);
670
671 /* Do an initial check. */
672 apm_periodic_check(sc);
673
674 /*
675 * Create a kernel thread to periodically check for APM events,
676 * and notify other subsystems when they occur.
677 */
678 if (kthread_create(PRI_NONE, 0, NULL, apm_thread, sc,
679 &sc->sc_thread, "%s", device_xname(sc->sc_dev)) != 0) {
680 /*
681 * We were unable to create the APM thread; bail out.
682 */
683 if (sc->sc_ops->aa_disconnect)
684 (*sc->sc_ops->aa_disconnect)(sc->sc_cookie);
685 aprint_error_dev(sc->sc_dev, "unable to create thread, "
686 "kernel APM support disabled\n");
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 = device_private(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 = device_private(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 = device_private(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 = device_private(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 = device_private(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