apm.c revision 1.14 1 /* $NetBSD: apm.c,v 1.14 2007/12/05 07:58:29 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.14 2007/12/05 07:58:29 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) mutex_enter(&(apmsc)->sc_lock)
106 #define APM_UNLOCK(apmsc) \
107 (void) mutex_exit(&(apmsc)->sc_lock)
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 u_int numbatts, capflags;
644
645 aprint_normal(": ");
646
647 switch ((APM_MAJOR_VERS(sc->sc_vers) << 8) + APM_MINOR_VERS(sc->sc_vers)) {
648 case 0x0100:
649 apm_v11_enabled = 0;
650 apm_v12_enabled = 0;
651 break;
652 case 0x0101:
653 apm_v12_enabled = 0;
654 /* fall through */
655 case 0x0102:
656 default:
657 break;
658 }
659
660 apm_set_ver(sc); /* prints version info */
661 aprint_normal("\n");
662 if (apm_minver >= 2)
663 (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, &numbatts,
664 &capflags);
665
666 /*
667 * enable power management
668 */
669 (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
670
671 if (sc->sc_ops->aa_cpu_busy)
672 (*sc->sc_ops->aa_cpu_busy)(sc->sc_cookie);
673
674 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
675
676 /* Initial state is `resumed'. */
677 sc->sc_power_state = PWR_RESUME;
678
679 /* Do an initial check. */
680 apm_periodic_check(sc);
681
682 /*
683 * Create a kernel thread to periodically check for APM events,
684 * and notify other subsystems when they occur.
685 */
686 if (kthread_create(PRI_NONE, 0, NULL, apm_thread, sc,
687 &sc->sc_thread, "%s", sc->sc_dev.dv_xname) != 0) {
688 /*
689 * We were unable to create the APM thread; bail out.
690 */
691 if (sc->sc_ops->aa_disconnect)
692 (*sc->sc_ops->aa_disconnect)(sc->sc_cookie);
693 printf("%s: unable to create thread, "
694 "kernel APM support disabled\n",
695 sc->sc_dev.dv_xname);
696 }
697 }
698
699 void
700 apm_thread(void *arg)
701 {
702 struct apm_softc *apmsc = arg;
703
704 /*
705 * Loop forever, doing a periodic check for APM events.
706 */
707 for (;;) {
708 APM_LOCK(apmsc);
709 apm_periodic_check(apmsc);
710 APM_UNLOCK(apmsc);
711 (void) tsleep(apmsc, PWAIT, "apmev", (8 * hz) / 7);
712 }
713 }
714
715 int
716 apmopen(dev_t dev, int flag, int mode, struct lwp *l)
717 {
718 int unit = APMUNIT(dev);
719 int ctl = APM(dev);
720 int error = 0;
721 struct apm_softc *sc;
722
723 if (unit >= apm_cd.cd_ndevs)
724 return ENXIO;
725 sc = apm_cd.cd_devs[unit];
726 if (!sc)
727 return ENXIO;
728
729 if (!apm_inited)
730 return ENXIO;
731
732 DPRINTF(APMDEBUG_DEVICE,
733 ("apmopen: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
734
735 APM_LOCK(sc);
736 switch (ctl) {
737 case APM_CTL:
738 if (!(flag & FWRITE)) {
739 error = EINVAL;
740 break;
741 }
742 if (sc->sc_flags & SCFLAG_OWRITE) {
743 error = EBUSY;
744 break;
745 }
746 sc->sc_flags |= SCFLAG_OWRITE;
747 break;
748 case APM_NORMAL:
749 if (!(flag & FREAD) || (flag & FWRITE)) {
750 error = EINVAL;
751 break;
752 }
753 sc->sc_flags |= SCFLAG_OREAD;
754 break;
755 default:
756 error = ENXIO;
757 break;
758 }
759 APM_UNLOCK(sc);
760
761 return (error);
762 }
763
764 int
765 apmclose(dev_t dev, int flag, int mode,
766 struct lwp *l)
767 {
768 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
769 int ctl = APM(dev);
770
771 DPRINTF(APMDEBUG_DEVICE,
772 ("apmclose: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
773
774 APM_LOCK(sc);
775 switch (ctl) {
776 case APM_CTL:
777 sc->sc_flags &= ~SCFLAG_OWRITE;
778 break;
779 case APM_NORMAL:
780 sc->sc_flags &= ~SCFLAG_OREAD;
781 break;
782 }
783 if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
784 sc->sc_event_count = 0;
785 sc->sc_event_ptr = 0;
786 }
787 APM_UNLOCK(sc);
788 return 0;
789 }
790
791 int
792 apmioctl(dev_t dev, u_long cmd, void *data, int flag,
793 struct lwp *l)
794 {
795 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
796 struct apm_power_info *powerp;
797 struct apm_event_info *evp;
798 #if 0
799 struct apm_ctl *actl;
800 #endif
801 int i, error = 0;
802 int batt_flags;
803 struct apm_ctl *actl;
804
805 APM_LOCK(sc);
806 switch (cmd) {
807 case APM_IOC_STANDBY:
808 if (!apm_do_standby) {
809 error = EOPNOTSUPP;
810 break;
811 }
812
813 if ((flag & FWRITE) == 0) {
814 error = EBADF;
815 break;
816 }
817 apm_userstandbys++;
818 break;
819
820 case APM_IOC_DEV_CTL:
821 actl = (struct apm_ctl *)data;
822 if ((flag & FWRITE) == 0) {
823 error = EBADF;
824 break;
825 }
826 #if 0
827 apm_get_powstate(actl->dev); /* XXX */
828 #endif
829 error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, actl->dev,
830 actl->mode);
831 apm_suspends++;
832 break;
833
834 case APM_IOC_SUSPEND:
835 if ((flag & FWRITE) == 0) {
836 error = EBADF;
837 break;
838 }
839 apm_suspends++;
840 break;
841
842 case APM_IOC_NEXTEVENT:
843 if (!sc->sc_event_count)
844 error = EAGAIN;
845 else {
846 evp = (struct apm_event_info *)data;
847 i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
848 i %= APM_NEVENTS;
849 *evp = sc->sc_event_list[i];
850 sc->sc_event_count--;
851 }
852 break;
853
854 case APM_IOC_GETPOWER:
855 powerp = (struct apm_power_info *)data;
856 if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0,
857 powerp)) != 0) {
858 apm_perror("ioctl get power status", error);
859 error = EIO;
860 break;
861 }
862 switch (apm_minver) {
863 case 0:
864 break;
865 case 1:
866 default:
867 batt_flags = powerp->battery_flags;
868 powerp->battery_state = APM_BATT_UNKNOWN;
869 if (batt_flags & APM_BATT_FLAG_HIGH)
870 powerp->battery_state = APM_BATT_HIGH;
871 else if (batt_flags & APM_BATT_FLAG_LOW)
872 powerp->battery_state = APM_BATT_LOW;
873 else if (batt_flags & APM_BATT_FLAG_CRITICAL)
874 powerp->battery_state = APM_BATT_CRITICAL;
875 else if (batt_flags & APM_BATT_FLAG_CHARGING)
876 powerp->battery_state = APM_BATT_CHARGING;
877 else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
878 powerp->battery_state = APM_BATT_ABSENT;
879 break;
880 }
881 break;
882
883 default:
884 error = ENOTTY;
885 }
886 APM_UNLOCK(sc);
887
888 return (error);
889 }
890
891 int
892 apmpoll(dev_t dev, int events, struct lwp *l)
893 {
894 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
895 int revents = 0;
896
897 APM_LOCK(sc);
898 if (events & (POLLIN | POLLRDNORM)) {
899 if (sc->sc_event_count)
900 revents |= events & (POLLIN | POLLRDNORM);
901 else
902 selrecord(l, &sc->sc_rsel);
903 }
904 APM_UNLOCK(sc);
905
906 return (revents);
907 }
908
909 static void
910 filt_apmrdetach(struct knote *kn)
911 {
912 struct apm_softc *sc = kn->kn_hook;
913
914 APM_LOCK(sc);
915 SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
916 APM_UNLOCK(sc);
917 }
918
919 static int
920 filt_apmread(struct knote *kn, long hint)
921 {
922 struct apm_softc *sc = kn->kn_hook;
923
924 kn->kn_data = sc->sc_event_count;
925 return (kn->kn_data > 0);
926 }
927
928 static const struct filterops apmread_filtops =
929 { 1, NULL, filt_apmrdetach, filt_apmread };
930
931 int
932 apmkqfilter(dev_t dev, struct knote *kn)
933 {
934 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
935 struct klist *klist;
936
937 switch (kn->kn_filter) {
938 case EVFILT_READ:
939 klist = &sc->sc_rsel.sel_klist;
940 kn->kn_fop = &apmread_filtops;
941 break;
942
943 default:
944 return (1);
945 }
946
947 kn->kn_hook = sc;
948
949 APM_LOCK(sc);
950 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
951 APM_UNLOCK(sc);
952
953 return (0);
954 }
955