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