apm.c revision 1.2.26.2 1 /* $NetBSD: apm.c,v 1.2.26.2 2007/09/03 14:33:24 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.2 2007/09/03 14:33:24 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/lock.h>
62 #include <sys/user.h>
63 #include <sys/malloc.h>
64 #include <sys/device.h>
65 #include <sys/fcntl.h>
66 #include <sys/ioctl.h>
67 #include <sys/select.h>
68 #include <sys/poll.h>
69 #include <sys/conf.h>
70
71 #include <dev/apm/apmvar.h>
72
73 #include <machine/stdarg.h>
74
75 #ifdef APMDEBUG
76 #define DPRINTF(f, x) do { if (apmdebug & (f)) printf x; } while (0)
77
78
79 #ifdef APMDEBUG_VALUE
80 int apmdebug = APMDEBUG_VALUE;
81 #else
82 int apmdebug = 0;
83 #endif /* APMDEBUG_VALUE */
84
85 #else
86 #define DPRINTF(f, x) /**/
87 #endif /* APMDEBUG */
88
89 #define SCFLAG_OREAD 0x0000001
90 #define SCFLAG_OWRITE 0x0000002
91 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
92
93 #define APMUNIT(dev) (minor(dev)&0xf0)
94 #define APM(dev) (minor(dev)&0x0f)
95 #define APM_NORMAL 0
96 #define APM_CTL 8
97
98 /*
99 * A brief note on the locking protocol: it's very simple; we
100 * assert an exclusive lock any time thread context enters the
101 * APM module. This is both the APM thread itself, as well as
102 * user context.
103 */
104 #define APM_LOCK(apmsc) \
105 (void) lockmgr(&(apmsc)->sc_lock, LK_EXCLUSIVE, NULL)
106 #define APM_UNLOCK(apmsc) \
107 (void) lockmgr(&(apmsc)->sc_lock, LK_RELEASE, NULL)
108
109 static void apm_event_handle(struct apm_softc *, u_int, u_int);
110 static void apm_periodic_check(struct apm_softc *);
111 static void apm_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 dopowerhooks(PWR_SOFTSUSPEND);
320
321 apm_spl = splhigh();
322
323 dopowerhooks(PWR_SUSPEND);
324 }
325
326 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
327 APM_SYS_SUSPEND);
328
329 if (error)
330 apm_resume(sc, 0, 0);
331 }
332
333 static void
334 apm_standby(struct apm_softc *sc)
335 {
336 int error;
337
338 if (sc->sc_power_state == PWR_STANDBY) {
339 #ifdef APMDEBUG
340 printf("%s: apm_standby: already standing by?\n",
341 sc->sc_dev.dv_xname);
342 #endif
343 return;
344 }
345 sc->sc_power_state = PWR_STANDBY;
346
347 if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) {
348 dopowerhooks(PWR_SOFTSTANDBY);
349
350 apm_spl = splhigh();
351
352 dopowerhooks(PWR_STANDBY);
353 }
354 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
355 APM_SYS_STANDBY);
356 if (error)
357 apm_resume(sc, 0, 0);
358 }
359
360 static void
361 apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info)
362 {
363
364 if (sc->sc_power_state == PWR_RESUME) {
365 #ifdef APMDEBUG
366 printf("%s: apm_resume: already running?\n",
367 sc->sc_dev.dv_xname);
368 #endif
369 return;
370 }
371 sc->sc_power_state = PWR_RESUME;
372
373 #ifdef TIMER_FREQ
374 /*
375 * Some system requires its clock to be initialized after hybernation.
376 */
377 initrtclock(TIMER_FREQ);
378 #endif
379
380 inittodr(time_second);
381 if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) {
382 dopowerhooks(PWR_RESUME);
383
384 splx(apm_spl);
385
386 dopowerhooks(PWR_SOFTRESUME);
387 }
388
389 apm_record_event(sc, event_type);
390 }
391
392 /*
393 * return 0 if the user will notice and handle the event,
394 * return 1 if the kernel driver should do so.
395 */
396 static int
397 apm_record_event(struct apm_softc *sc, u_int event_type)
398 {
399 struct apm_event_info *evp;
400
401 if ((sc->sc_flags & SCFLAG_OPEN) == 0)
402 return 1; /* no user waiting */
403 if (sc->sc_event_count == APM_NEVENTS)
404 return 1; /* overflow */
405 evp = &sc->sc_event_list[sc->sc_event_ptr];
406 sc->sc_event_count++;
407 sc->sc_event_ptr++;
408 sc->sc_event_ptr %= APM_NEVENTS;
409 evp->type = event_type;
410 evp->index = ++apm_evindex;
411 selnotify(&sc->sc_rsel, 0);
412 return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */
413 }
414
415 static void
416 apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info)
417 {
418 int error;
419 const char *code;
420 struct apm_power_info pi;
421
422 switch (event_code) {
423 case APM_USER_STANDBY_REQ:
424 DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n"));
425 if (apm_do_standby) {
426 if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
427 apm_userstandbys++;
428 apm_op_inprog++;
429 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
430 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
431 } else {
432 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
433 APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
434 /* in case BIOS hates being spurned */
435 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
436 }
437 break;
438
439 case APM_STANDBY_REQ:
440 DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n"));
441 if (apm_standbys || apm_suspends) {
442 DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
443 ("damn fool BIOS did not wait for answer\n"));
444 /* just give up the fight */
445 apm_damn_fool_bios = 1;
446 }
447 if (apm_do_standby) {
448 if (apm_op_inprog == 0 &&
449 apm_record_event(sc, event_code))
450 apm_standbys++;
451 apm_op_inprog++;
452 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
453 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
454 } else {
455 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
456 APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
457 /* in case BIOS hates being spurned */
458 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
459 }
460 break;
461
462 case APM_USER_SUSPEND_REQ:
463 DPRINTF(APMDEBUG_EVENTS, ("apmev: user suspend request\n"));
464 if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
465 apm_suspends++;
466 apm_op_inprog++;
467 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
468 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
469 break;
470
471 case APM_SUSPEND_REQ:
472 DPRINTF(APMDEBUG_EVENTS, ("apmev: system suspend request\n"));
473 if (apm_standbys || apm_suspends) {
474 DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
475 ("damn fool BIOS did not wait for answer\n"));
476 /* just give up the fight */
477 apm_damn_fool_bios = 1;
478 }
479 if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
480 apm_suspends++;
481 apm_op_inprog++;
482 (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
483 APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
484 break;
485
486 case APM_POWER_CHANGE:
487 DPRINTF(APMDEBUG_EVENTS, ("apmev: power status change\n"));
488 error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
489 #ifdef APM_POWER_PRINT
490 /* only print if nobody is catching events. */
491 if (error == 0 &&
492 (sc->sc_flags & (SCFLAG_OREAD|SCFLAG_OWRITE)) == 0)
493 apm_power_print(sc, &pi);
494 #endif
495 apm_record_event(sc, event_code);
496 break;
497
498 case APM_NORMAL_RESUME:
499 DPRINTF(APMDEBUG_EVENTS, ("apmev: resume system\n"));
500 apm_resume(sc, event_code, event_info);
501 break;
502
503 case APM_CRIT_RESUME:
504 DPRINTF(APMDEBUG_EVENTS, ("apmev: critical resume system"));
505 apm_resume(sc, event_code, event_info);
506 break;
507
508 case APM_SYS_STANDBY_RESUME:
509 DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby resume\n"));
510 apm_resume(sc, event_code, event_info);
511 break;
512
513 case APM_UPDATE_TIME:
514 DPRINTF(APMDEBUG_EVENTS, ("apmev: update time\n"));
515 apm_resume(sc, event_code, event_info);
516 break;
517
518 case APM_CRIT_SUSPEND_REQ:
519 DPRINTF(APMDEBUG_EVENTS, ("apmev: critical system suspend\n"));
520 apm_record_event(sc, event_code);
521 apm_suspend(sc);
522 break;
523
524 case APM_BATTERY_LOW:
525 DPRINTF(APMDEBUG_EVENTS, ("apmev: battery low\n"));
526 apm_battlow++;
527 apm_record_event(sc, event_code);
528 break;
529
530 case APM_CAP_CHANGE:
531 DPRINTF(APMDEBUG_EVENTS, ("apmev: capability change\n"));
532 if (apm_minver < 2) {
533 DPRINTF(APMDEBUG_EVENTS, ("apm: unexpected event\n"));
534 } else {
535 u_int numbatts, capflags;
536 (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie,
537 &numbatts, &capflags);
538 (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
539 }
540 break;
541
542 default:
543 switch (event_code >> 8) {
544 case 0:
545 code = "reserved system";
546 break;
547 case 1:
548 code = "reserved device";
549 break;
550 case 2:
551 code = "OEM defined";
552 break;
553 default:
554 code = "reserved";
555 break;
556 }
557 printf("APM: %s event code %x\n", code, event_code);
558 }
559 }
560
561 static void
562 apm_periodic_check(struct apm_softc *sc)
563 {
564 int error;
565 u_int event_code, event_info;
566
567
568 /*
569 * tell the BIOS we're working on it, if asked to do a
570 * suspend/standby
571 */
572 if (apm_op_inprog)
573 (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
574 APM_LASTREQ_INPROG);
575
576 while ((error = (*sc->sc_ops->aa_get_event)(sc->sc_cookie, &event_code,
577 &event_info)) == 0 && !apm_damn_fool_bios)
578 apm_event_handle(sc, event_code, event_info);
579
580 if (error != APM_ERR_NOEVENTS)
581 apm_perror("get event", error);
582 if (apm_suspends) {
583 apm_op_inprog = 0;
584 apm_suspend(sc);
585 } else if (apm_standbys || apm_userstandbys) {
586 apm_op_inprog = 0;
587 apm_standby(sc);
588 }
589 apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
590 apm_damn_fool_bios = 0;
591 }
592
593 static void
594 apm_set_ver(struct apm_softc *sc)
595 {
596
597 if (apm_v12_enabled &&
598 APM_MAJOR_VERS(sc->sc_vers) == 1 &&
599 APM_MINOR_VERS(sc->sc_vers) == 2) {
600 apm_majver = 1;
601 apm_minver = 2;
602 goto ok;
603 }
604
605 if (apm_v11_enabled &&
606 APM_MAJOR_VERS(sc->sc_vers) == 1 &&
607 APM_MINOR_VERS(sc->sc_vers) == 1) {
608 apm_majver = 1;
609 apm_minver = 1;
610 } else {
611 apm_majver = 1;
612 apm_minver = 0;
613 }
614 ok:
615 aprint_normal("Power Management spec V%d.%d", apm_majver, apm_minver);
616 apm_inited = 1;
617 if (sc->sc_detail & APM_IDLE_SLOWS) {
618 #ifdef DIAGNOSTIC
619 /* not relevant often */
620 aprint_normal(" (slowidle)");
621 #endif
622 /* leave apm_do_idle at its user-configured setting */
623 } else
624 apm_do_idle = 0;
625 #ifdef DIAGNOSTIC
626 if (sc->sc_detail & APM_BIOS_PM_DISABLED)
627 aprint_normal(" (BIOS mgmt disabled)");
628 if (sc->sc_detail & APM_BIOS_PM_DISENGAGED)
629 aprint_normal(" (BIOS managing devices)");
630 #endif
631 }
632
633 int
634 apm_match(void)
635 {
636 static int got;
637 return !got++;
638 }
639
640 void
641 apm_attach(struct apm_softc *sc)
642 {
643 struct apm_power_info pinfo;
644 u_int numbatts, capflags;
645 int error;
646
647 aprint_normal(": ");
648
649 switch ((APM_MAJOR_VERS(sc->sc_vers) << 8) + APM_MINOR_VERS(sc->sc_vers)) {
650 case 0x0100:
651 apm_v11_enabled = 0;
652 apm_v12_enabled = 0;
653 break;
654 case 0x0101:
655 apm_v12_enabled = 0;
656 /* fall through */
657 case 0x0102:
658 default:
659 break;
660 }
661
662 apm_set_ver(sc); /* prints version info */
663 aprint_normal("\n");
664 if (apm_minver >= 2)
665 (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, &numbatts,
666 &capflags);
667
668 /*
669 * enable power management
670 */
671 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
672
673 error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pinfo);
674 if (error == 0) {
675 #ifdef APM_POWER_PRINT
676 apm_power_print(sc, &pinfo);
677 #endif
678 } else
679 apm_perror("get power status", error);
680 if (sc->sc_ops->aa_cpu_busy)
681 (*sc->sc_ops->aa_cpu_busy)(sc->sc_cookie);
682
683 lockinit(&sc->sc_lock, PWAIT, "apmlk", 0, 0);
684
685 /* Initial state is `resumed'. */
686 sc->sc_power_state = PWR_RESUME;
687
688 /* Do an initial check. */
689 apm_periodic_check(sc);
690
691 /*
692 * Create a kernel thread to periodically check for APM events,
693 * and notify other subsystems when they occur.
694 */
695 if (kthread_create(PRI_NONE, 0, NULL, apm_thread, sc,
696 &sc->sc_thread, "%s", sc->sc_dev.dv_xname) != 0) {
697 /*
698 * We were unable to create the APM thread; bail out.
699 */
700 if (sc->sc_ops->aa_disconnect)
701 (*sc->sc_ops->aa_disconnect)(sc->sc_cookie);
702 printf("%s: unable to create thread, "
703 "kernel APM support disabled\n",
704 sc->sc_dev.dv_xname);
705 }
706 }
707
708 void
709 apm_thread(void *arg)
710 {
711 struct apm_softc *apmsc = arg;
712
713 /*
714 * Loop forever, doing a periodic check for APM events.
715 */
716 for (;;) {
717 APM_LOCK(apmsc);
718 apm_periodic_check(apmsc);
719 APM_UNLOCK(apmsc);
720 (void) tsleep(apmsc, PWAIT, "apmev", (8 * hz) / 7);
721 }
722 }
723
724 int
725 apmopen(dev_t dev, int flag, int mode, struct lwp *l)
726 {
727 int unit = APMUNIT(dev);
728 int ctl = APM(dev);
729 int error = 0;
730 struct apm_softc *sc;
731
732 if (unit >= apm_cd.cd_ndevs)
733 return ENXIO;
734 sc = apm_cd.cd_devs[unit];
735 if (!sc)
736 return ENXIO;
737
738 if (!apm_inited)
739 return ENXIO;
740
741 DPRINTF(APMDEBUG_DEVICE,
742 ("apmopen: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
743
744 APM_LOCK(sc);
745 switch (ctl) {
746 case APM_CTL:
747 if (!(flag & FWRITE)) {
748 error = EINVAL;
749 break;
750 }
751 if (sc->sc_flags & SCFLAG_OWRITE) {
752 error = EBUSY;
753 break;
754 }
755 sc->sc_flags |= SCFLAG_OWRITE;
756 break;
757 case APM_NORMAL:
758 if (!(flag & FREAD) || (flag & FWRITE)) {
759 error = EINVAL;
760 break;
761 }
762 sc->sc_flags |= SCFLAG_OREAD;
763 break;
764 default:
765 error = ENXIO;
766 break;
767 }
768 APM_UNLOCK(sc);
769
770 return (error);
771 }
772
773 int
774 apmclose(dev_t dev, int flag, int mode,
775 struct lwp *l)
776 {
777 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
778 int ctl = APM(dev);
779
780 DPRINTF(APMDEBUG_DEVICE,
781 ("apmclose: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
782
783 APM_LOCK(sc);
784 switch (ctl) {
785 case APM_CTL:
786 sc->sc_flags &= ~SCFLAG_OWRITE;
787 break;
788 case APM_NORMAL:
789 sc->sc_flags &= ~SCFLAG_OREAD;
790 break;
791 }
792 if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
793 sc->sc_event_count = 0;
794 sc->sc_event_ptr = 0;
795 }
796 APM_UNLOCK(sc);
797 return 0;
798 }
799
800 int
801 apmioctl(dev_t dev, u_long cmd, void *data, int flag,
802 struct lwp *l)
803 {
804 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
805 struct apm_power_info *powerp;
806 struct apm_event_info *evp;
807 #if 0
808 struct apm_ctl *actl;
809 #endif
810 int i, error = 0;
811 int batt_flags;
812 struct apm_ctl *actl;
813
814 APM_LOCK(sc);
815 switch (cmd) {
816 case APM_IOC_STANDBY:
817 if (!apm_do_standby) {
818 error = EOPNOTSUPP;
819 break;
820 }
821
822 if ((flag & FWRITE) == 0) {
823 error = EBADF;
824 break;
825 }
826 apm_userstandbys++;
827 break;
828
829 case APM_IOC_DEV_CTL:
830 actl = (struct apm_ctl *)data;
831 if ((flag & FWRITE) == 0) {
832 error = EBADF;
833 break;
834 }
835 #if 0
836 apm_get_powstate(actl->dev); /* XXX */
837 #endif
838 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, actl->dev,
839 actl->mode);
840 apm_suspends++;
841 break;
842
843 case APM_IOC_SUSPEND:
844 if ((flag & FWRITE) == 0) {
845 error = EBADF;
846 break;
847 }
848 apm_suspends++;
849 break;
850
851 case APM_IOC_NEXTEVENT:
852 if (!sc->sc_event_count)
853 error = EAGAIN;
854 else {
855 evp = (struct apm_event_info *)data;
856 i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
857 i %= APM_NEVENTS;
858 *evp = sc->sc_event_list[i];
859 sc->sc_event_count--;
860 }
861 break;
862
863 case APM_IOC_GETPOWER:
864 powerp = (struct apm_power_info *)data;
865 if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0,
866 powerp)) != 0) {
867 apm_perror("ioctl get power status", error);
868 error = EIO;
869 break;
870 }
871 switch (apm_minver) {
872 case 0:
873 break;
874 case 1:
875 default:
876 batt_flags = powerp->battery_flags;
877 powerp->battery_state = APM_BATT_UNKNOWN;
878 if (batt_flags & APM_BATT_FLAG_HIGH)
879 powerp->battery_state = APM_BATT_HIGH;
880 else if (batt_flags & APM_BATT_FLAG_LOW)
881 powerp->battery_state = APM_BATT_LOW;
882 else if (batt_flags & APM_BATT_FLAG_CRITICAL)
883 powerp->battery_state = APM_BATT_CRITICAL;
884 else if (batt_flags & APM_BATT_FLAG_CHARGING)
885 powerp->battery_state = APM_BATT_CHARGING;
886 else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
887 powerp->battery_state = APM_BATT_ABSENT;
888 break;
889 }
890 break;
891
892 default:
893 error = ENOTTY;
894 }
895 APM_UNLOCK(sc);
896
897 return (error);
898 }
899
900 int
901 apmpoll(dev_t dev, int events, struct lwp *l)
902 {
903 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
904 int revents = 0;
905
906 APM_LOCK(sc);
907 if (events & (POLLIN | POLLRDNORM)) {
908 if (sc->sc_event_count)
909 revents |= events & (POLLIN | POLLRDNORM);
910 else
911 selrecord(l, &sc->sc_rsel);
912 }
913 APM_UNLOCK(sc);
914
915 return (revents);
916 }
917
918 static void
919 filt_apmrdetach(struct knote *kn)
920 {
921 struct apm_softc *sc = kn->kn_hook;
922
923 APM_LOCK(sc);
924 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
925 APM_UNLOCK(sc);
926 }
927
928 static int
929 filt_apmread(struct knote *kn, long hint)
930 {
931 struct apm_softc *sc = kn->kn_hook;
932
933 kn->kn_data = sc->sc_event_count;
934 return (kn->kn_data > 0);
935 }
936
937 static const struct filterops apmread_filtops =
938 { 1, NULL, filt_apmrdetach, filt_apmread };
939
940 int
941 apmkqfilter(dev_t dev, struct knote *kn)
942 {
943 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
944 struct klist *klist;
945
946 switch (kn->kn_filter) {
947 case EVFILT_READ:
948 klist = &sc->sc_rsel.sel_klist;
949 kn->kn_fop = &apmread_filtops;
950 break;
951
952 default:
953 return (1);
954 }
955
956 kn->kn_hook = sc;
957
958 APM_LOCK(sc);
959 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
960 APM_UNLOCK(sc);
961
962 return (0);
963 }
964