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