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