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