1 /* $NetBSD: apmdev.c,v 1.23 2009/04/03 05:01:10 uwe Exp $ */
4 * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by John Kohl and Christopher G. Demetriou.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
32 * from: sys/arch/i386/i386/apm.c,v 1.49 2000/05/08
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: apmdev.c,v 1.23 2009/04/03 05:01:10 uwe Exp $");
43 #error APM_NOIDLE option deprecated; use APM_NO_IDLE instead
46 #if defined(DEBUG) && !defined(APMDEBUG)
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/signalvar.h>
53 #include <sys/kernel.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>
64 #include <dev/hpc/apm/apmvar.h>
66 #include <machine/stdarg.h>
69 #define DPRINTF(f, x) do { if (apmdebug & (f)) printf x; } while (0)
73 int apmdebug
= APMDEBUG_VALUE
;
76 #endif /* APMDEBUG_VALUE */
79 #define DPRINTF(f, x) /**/
82 #define SCFLAG_OREAD 0x0000001
83 #define SCFLAG_OWRITE 0x0000002
84 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
86 #define APMUNIT(dev) (minor(dev)&0xf0)
87 #define APM(dev) (minor(dev)&0x0f)
92 * A brief note on the locking protocol: it's very simple; we
93 * assert an exclusive lock any time thread context enters the
94 * APM module. This is both the APM thread itself, as well as
97 #define APM_LOCK(apmsc) \
98 (void) mutex_enter(&(apmsc)->sc_lock)
99 #define APM_UNLOCK(apmsc) \
100 (void) mutex_exit(&(apmsc)->sc_lock)
102 static void apmdevattach(device_t
, device_t
, void *);
103 static int apmdevmatch(device_t
, cfdata_t
, void *);
105 static void apm_event_handle(struct apm_softc
*, u_int
, u_int
);
106 static void apm_periodic_check(struct apm_softc
*);
107 static void apm_thread(void *);
108 static void apm_perror(const char *, int, ...)
109 __attribute__((__format__(__printf__
,1,3)));
110 #ifdef APM_POWER_PRINT
111 static void apm_power_print(struct apm_softc
*, struct apm_power_info
*);
113 static int apm_record_event(struct apm_softc
*, u_int
);
114 static void apm_set_ver(struct apm_softc
*);
115 static void apm_standby(struct apm_softc
*);
116 static void apm_suspend(struct apm_softc
*);
117 static void apm_resume(struct apm_softc
*, u_int
, u_int
);
119 CFATTACH_DECL_NEW(apmdev
, sizeof(struct apm_softc
),
120 apmdevmatch
, apmdevattach
, NULL
, NULL
);
122 extern struct cfdriver apmdev_cd
;
124 dev_type_open(apmdevopen
);
125 dev_type_close(apmdevclose
);
126 dev_type_ioctl(apmdevioctl
);
127 dev_type_poll(apmdevpoll
);
128 dev_type_kqfilter(apmdevkqfilter
);
130 const struct cdevsw apmdev_cdevsw
= {
131 apmdevopen
, apmdevclose
, noread
, nowrite
, apmdevioctl
,
132 nostop
, notty
, apmdevpoll
, nommap
, apmdevkqfilter
, D_OTHER
135 /* configurable variables */
136 int apm_bogus_bios
= 0;
147 #ifdef APM_NO_STANDBY
148 int apm_do_standby
= 0;
150 int apm_do_standby
= 1;
153 int apm_v11_enabled
= 0;
155 int apm_v11_enabled
= 1;
158 int apm_v12_enabled
= 0;
160 int apm_v12_enabled
= 1;
163 /* variables used during operation (XXX cgd) */
164 u_char apm_majver
, apm_minver
;
166 int apm_standbys
, apm_userstandbys
, apm_suspends
, apm_battlow
;
167 int apm_damn_fool_bios
, apm_op_inprog
;
170 static int apm_spl
; /* saved spl while suspended */
173 apm_strerror(int code
)
176 case APM_ERR_PM_DISABLED
:
177 return ("power management disabled");
178 case APM_ERR_REALALREADY
:
179 return ("real mode interface already connected");
180 case APM_ERR_NOTCONN
:
181 return ("interface not connected");
182 case APM_ERR_16ALREADY
:
183 return ("16-bit interface already connected");
184 case APM_ERR_16NOTSUPP
:
185 return ("16-bit interface not supported");
186 case APM_ERR_32ALREADY
:
187 return ("32-bit interface already connected");
188 case APM_ERR_32NOTSUPP
:
189 return ("32-bit interface not supported");
190 case APM_ERR_UNRECOG_DEV
:
191 return ("unrecognized device ID");
193 return ("parameter out of range");
194 case APM_ERR_NOTENGAGED
:
195 return ("interface not engaged");
197 return ("unable to enter requested state");
198 case APM_ERR_NOEVENTS
:
199 return ("no pending events");
200 case APM_ERR_NOT_PRESENT
:
201 return ("no APM present");
203 return ("unknown error code");
208 apm_perror(const char *str
, int errinfo
, ...) /* XXX cgd */
214 va_start(ap
, errinfo
);
215 vprintf(str
, ap
); /* XXX cgd */
218 printf(": %s\n", apm_strerror(errinfo
));
221 #ifdef APM_POWER_PRINT
223 apm_power_print(struct apm_softc
*sc
, struct apm_power_info
*pi
)
226 if (pi
->battery_life
!= APM_BATT_LIFE_UNKNOWN
) {
227 aprint_normal_dev(sc
->sc_dev
,
228 "battery life expectancy: %d%%\n",
231 aprint_normal_dev(sc
->sc_dev
, "A/C state: ");
232 switch (pi
->ac_state
) {
240 printf("backup power\n");
247 aprint_normal_dev(sc
->sc_dev
, "battery charge state:");
249 switch (pi
->battery_state
) {
256 case APM_BATT_CRITICAL
:
257 printf("critical\n");
259 case APM_BATT_CHARGING
:
260 printf("charging\n");
262 case APM_BATT_UNKNOWN
:
266 printf("undecoded state %x\n", pi
->battery_state
);
269 else if (apm_minver
>= 1) {
270 if (pi
->battery_flags
& APM_BATT_FLAG_NO_SYSTEM_BATTERY
)
271 printf(" no battery");
273 if (pi
->battery_flags
& APM_BATT_FLAG_HIGH
)
275 if (pi
->battery_flags
& APM_BATT_FLAG_LOW
)
277 if (pi
->battery_flags
& APM_BATT_FLAG_CRITICAL
)
279 if (pi
->battery_flags
& APM_BATT_FLAG_CHARGING
)
283 if (pi
->minutes_valid
) {
284 aprint_normal_dev(sc
->sc_dev
, "estimated ");
285 if (pi
->minutes_left
/ 60)
286 printf("%dh ", pi
->minutes_left
/ 60);
287 printf("%dm\n", pi
->minutes_left
% 60);
295 apm_suspend(struct apm_softc
*sc
)
299 if (sc
->sc_power_state
== PWR_SUSPEND
) {
301 aprint_debug_dev(sc
->sc_dev
,
302 "apm_suspend: already suspended?\n");
306 sc
->sc_power_state
= PWR_SUSPEND
;
308 dopowerhooks(PWR_SOFTSUSPEND
);
309 (void) tsleep(sc
, PWAIT
, "apmsuspend", hz
/2);
313 dopowerhooks(PWR_SUSPEND
);
315 error
= (*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
, APM_DEV_ALLDEVS
,
319 apm_resume(sc
, 0, 0);
323 apm_standby(struct apm_softc
*sc
)
327 if (sc
->sc_power_state
== PWR_STANDBY
) {
329 aprint_debug_dev(sc
->sc_dev
,
330 "apm_standby: already standing by?\n");
334 sc
->sc_power_state
= PWR_STANDBY
;
336 dopowerhooks(PWR_SOFTSTANDBY
);
337 (void) tsleep(sc
, PWAIT
, "apmstandby", hz
/2);
341 dopowerhooks(PWR_STANDBY
);
343 error
= (*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
, APM_DEV_ALLDEVS
,
346 apm_resume(sc
, 0, 0);
350 apm_resume(struct apm_softc
*sc
, u_int event_type
, u_int event_info
)
353 if (sc
->sc_power_state
== PWR_RESUME
) {
355 aprint_debug_dev(sc
->sc_dev
, "apm_resume: already running?\n");
359 sc
->sc_power_state
= PWR_RESUME
;
361 #if 0 /* XXX: def TIME_FREQ */
363 * Some system requires its clock to be initialized after hybernation.
365 initrtclock(TIMER_FREQ
);
368 inittodr(time_second
);
369 dopowerhooks(PWR_RESUME
);
373 dopowerhooks(PWR_SOFTRESUME
);
375 apm_record_event(sc
, event_type
);
379 * return 0 if the user will notice and handle the event,
380 * return 1 if the kernel driver should do so.
383 apm_record_event(struct apm_softc
*sc
, u_int event_type
)
385 struct apm_event_info
*evp
;
387 if ((sc
->sc_flags
& SCFLAG_OPEN
) == 0)
388 return 1; /* no user waiting */
389 if (sc
->sc_event_count
== APM_NEVENTS
)
390 return 1; /* overflow */
391 evp
= &sc
->sc_event_list
[sc
->sc_event_ptr
];
392 sc
->sc_event_count
++;
394 sc
->sc_event_ptr
%= APM_NEVENTS
;
395 evp
->type
= event_type
;
396 evp
->index
= ++apm_evindex
;
397 selnotify(&sc
->sc_rsel
, 0, 0);
398 return (sc
->sc_flags
& SCFLAG_OWRITE
) ? 0 : 1; /* user may handle */
402 apm_event_handle(struct apm_softc
*sc
, u_int event_code
, u_int event_info
)
406 struct apm_power_info pi
;
408 switch (event_code
) {
409 case APM_USER_STANDBY_REQ
:
410 DPRINTF(APMDEBUG_EVENTS
, ("apmev: user standby request\n"));
411 if (apm_do_standby
) {
412 if (apm_op_inprog
== 0 && apm_record_event(sc
, event_code
))
415 (void)(*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
,
416 APM_DEV_ALLDEVS
, APM_LASTREQ_INPROG
);
418 (void)(*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
,
419 APM_DEV_ALLDEVS
, APM_LASTREQ_REJECTED
);
420 /* in case BIOS hates being spurned */
421 (*sc
->sc_ops
->aa_enable
)(sc
->sc_cookie
, 1);
425 case APM_STANDBY_REQ
:
426 DPRINTF(APMDEBUG_EVENTS
, ("apmev: system standby request\n"));
427 if (apm_standbys
|| apm_suspends
) {
428 DPRINTF(APMDEBUG_EVENTS
| APMDEBUG_ANOM
,
429 ("damn fool BIOS did not wait for answer\n"));
430 /* just give up the fight */
431 apm_damn_fool_bios
= 1;
433 if (apm_do_standby
) {
434 if (apm_op_inprog
== 0 &&
435 apm_record_event(sc
, event_code
))
438 (void)(*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
,
439 APM_DEV_ALLDEVS
, APM_LASTREQ_INPROG
);
441 (void)(*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
,
442 APM_DEV_ALLDEVS
, APM_LASTREQ_REJECTED
);
443 /* in case BIOS hates being spurned */
444 (*sc
->sc_ops
->aa_enable
)(sc
->sc_cookie
, 1);
448 case APM_USER_SUSPEND_REQ
:
449 DPRINTF(APMDEBUG_EVENTS
, ("apmev: user suspend request\n"));
450 if (apm_op_inprog
== 0 && apm_record_event(sc
, event_code
))
453 (void)(*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
,
454 APM_DEV_ALLDEVS
, APM_LASTREQ_INPROG
);
457 case APM_SUSPEND_REQ
:
458 DPRINTF(APMDEBUG_EVENTS
, ("apmev: system suspend request\n"));
459 if (apm_standbys
|| apm_suspends
) {
460 DPRINTF(APMDEBUG_EVENTS
| APMDEBUG_ANOM
,
461 ("damn fool BIOS did not wait for answer\n"));
462 /* just give up the fight */
463 apm_damn_fool_bios
= 1;
465 if (apm_op_inprog
== 0 && apm_record_event(sc
, event_code
))
468 (void)(*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
,
469 APM_DEV_ALLDEVS
, APM_LASTREQ_INPROG
);
472 case APM_POWER_CHANGE
:
473 DPRINTF(APMDEBUG_EVENTS
, ("apmev: power status change\n"));
474 error
= (*sc
->sc_ops
->aa_get_powstat
)(sc
->sc_cookie
, 0, &pi
);
475 #ifdef APM_POWER_PRINT
476 /* only print if nobody is catching events. */
478 (sc
->sc_flags
& (SCFLAG_OREAD
|SCFLAG_OWRITE
)) == 0)
479 apm_power_print(sc
, &pi
);
481 apm_record_event(sc
, event_code
);
484 case APM_NORMAL_RESUME
:
485 DPRINTF(APMDEBUG_EVENTS
, ("apmev: resume system\n"));
486 apm_resume(sc
, event_code
, event_info
);
489 case APM_CRIT_RESUME
:
490 DPRINTF(APMDEBUG_EVENTS
, ("apmev: critical resume system"));
491 apm_resume(sc
, event_code
, event_info
);
494 case APM_SYS_STANDBY_RESUME
:
495 DPRINTF(APMDEBUG_EVENTS
, ("apmev: system standby resume\n"));
496 apm_resume(sc
, event_code
, event_info
);
499 case APM_UPDATE_TIME
:
500 DPRINTF(APMDEBUG_EVENTS
, ("apmev: update time\n"));
501 apm_resume(sc
, event_code
, event_info
);
504 case APM_CRIT_SUSPEND_REQ
:
505 DPRINTF(APMDEBUG_EVENTS
, ("apmev: critical system suspend\n"));
506 apm_record_event(sc
, event_code
);
510 case APM_BATTERY_LOW
:
511 DPRINTF(APMDEBUG_EVENTS
, ("apmev: battery low\n"));
513 apm_record_event(sc
, event_code
);
517 DPRINTF(APMDEBUG_EVENTS
, ("apmev: capability change\n"));
518 if (apm_minver
< 2) {
519 DPRINTF(APMDEBUG_EVENTS
, ("apm: unexpected event\n"));
521 u_int numbatts
, capflags
;
522 (*sc
->sc_ops
->aa_get_capabilities
)(sc
->sc_cookie
,
523 &numbatts
, &capflags
);
524 (*sc
->sc_ops
->aa_get_powstat
)(sc
->sc_cookie
, 0, &pi
);
529 switch (event_code
>> 8) {
531 code
= "reserved system";
534 code
= "reserved device";
537 code
= "OEM defined";
543 printf("APM: %s event code %x\n", code
, event_code
);
548 apm_periodic_check(struct apm_softc
*sc
)
551 u_int event_code
, event_info
;
555 * tell the BIOS we're working on it, if asked to do a
559 (*sc
->sc_ops
->aa_set_powstate
)(sc
->sc_cookie
, APM_DEV_ALLDEVS
,
562 while ((error
= (*sc
->sc_ops
->aa_get_event
)(sc
->sc_cookie
, &event_code
,
563 &event_info
)) == 0 && !apm_damn_fool_bios
)
564 apm_event_handle(sc
, event_code
, event_info
);
566 if (error
!= APM_ERR_NOEVENTS
)
567 apm_perror("get event", error
);
571 } else if (apm_standbys
|| apm_userstandbys
) {
575 apm_suspends
= apm_standbys
= apm_battlow
= apm_userstandbys
= 0;
576 apm_damn_fool_bios
= 0;
580 apm_set_ver(struct apm_softc
*sc
)
583 if (apm_v12_enabled
&&
584 APM_MAJOR_VERS(sc
->sc_vers
) == 1 &&
585 APM_MINOR_VERS(sc
->sc_vers
) == 2) {
591 if (apm_v11_enabled
&&
592 APM_MAJOR_VERS(sc
->sc_vers
) == 1 &&
593 APM_MINOR_VERS(sc
->sc_vers
) == 1) {
601 aprint_normal("Power Management spec V%d.%d", apm_majver
, apm_minver
);
603 if (sc
->sc_detail
& APM_IDLE_SLOWS
) {
605 /* not relevant often */
606 aprint_normal(" (slowidle)");
608 /* leave apm_do_idle at its user-configured setting */
612 if (sc
->sc_detail
& APM_BIOS_PM_DISABLED
)
613 aprint_normal(" (BIOS mgmt disabled)");
614 if (sc
->sc_detail
& APM_BIOS_PM_DISENGAGED
)
615 aprint_normal(" (BIOS managing devices)");
620 apmdevmatch(device_t parent
, cfdata_t match
, void *aux
)
627 apmdevattach(device_t parent
, device_t self
, void *aux
)
629 struct apm_softc
*sc
;
630 struct apmdev_attach_args
*aaa
= aux
;
632 sc
= device_private(self
);
635 sc
->sc_detail
= aaa
->apm_detail
;
636 sc
->sc_vers
= aaa
->apm_detail
& 0xffff; /* XXX: magic */
638 sc
->sc_ops
= aaa
->accessops
;
639 sc
->sc_cookie
= aaa
->accesscookie
;
645 * Print function (for parent devices).
648 apmprint(void *aux
, const char *pnp
)
651 aprint_normal("apm at %s", pnp
);
664 apm_attach(struct apm_softc
*sc
)
666 struct apm_power_info pinfo
;
667 u_int numbatts
, capflags
;
673 switch ((APM_MAJOR_VERS(sc
->sc_vers
) << 8) + APM_MINOR_VERS(sc
->sc_vers
)) {
686 apm_set_ver(sc
); /* prints version info */
689 (*sc
->sc_ops
->aa_get_capabilities
)(sc
->sc_cookie
, &numbatts
,
693 * enable power management
695 (*sc
->sc_ops
->aa_enable
)(sc
->sc_cookie
, 1);
697 error
= (*sc
->sc_ops
->aa_get_powstat
)(sc
->sc_cookie
, 0, &pinfo
);
699 #ifdef APM_POWER_PRINT
700 apm_power_print(sc
, &pinfo
);
703 apm_perror("get power status", error
);
705 if (sc
->sc_ops
->aa_cpu_busy
)
706 (*sc
->sc_ops
->aa_cpu_busy
)(sc
->sc_cookie
);
708 mutex_init(&sc
->sc_lock
, MUTEX_DEFAULT
, IPL_NONE
);
710 /* Initial state is `resumed'. */
711 sc
->sc_power_state
= PWR_RESUME
;
712 selinit(&sc
->sc_rsel
);
713 selinit(&sc
->sc_xsel
);
715 /* Do an initial check. */
716 apm_periodic_check(sc
);
719 * Create a kernel thread to periodically check for APM events,
720 * and notify other subsystems when they occur.
722 if (kthread_create(PRI_NONE
, 0, NULL
, apm_thread
, sc
,
723 &sc
->sc_thread
, "%s", device_xname(sc
->sc_dev
)) != 0) {
725 * We were unable to create the APM thread; bail out.
727 if (sc
->sc_ops
->aa_disconnect
)
728 (*sc
->sc_ops
->aa_disconnect
)(sc
->sc_cookie
);
729 aprint_error_dev(sc
->sc_dev
, "unable to create thread, "
730 "kernel APM support disabled\n");
735 apm_thread(void *arg
)
737 struct apm_softc
*apmsc
= arg
;
740 * Loop forever, doing a periodic check for APM events.
744 apm_periodic_check(apmsc
);
746 (void) tsleep(apmsc
, PWAIT
, "apmev", (8 * hz
) / 7);
751 apmdevopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
755 struct apm_softc
*sc
;
757 sc
= device_lookup_private(&apmdev_cd
, APMUNIT(dev
));
764 DPRINTF(APMDEBUG_DEVICE
,
765 ("apmopen: pid %d flag %x mode %x\n", l
->l_proc
->p_pid
, flag
, mode
));
770 if (!(flag
& FWRITE
)) {
774 if (sc
->sc_flags
& SCFLAG_OWRITE
) {
778 sc
->sc_flags
|= SCFLAG_OWRITE
;
781 if (!(flag
& FREAD
) || (flag
& FWRITE
)) {
785 sc
->sc_flags
|= SCFLAG_OREAD
;
797 apmdevclose(dev_t dev
, int flag
, int mode
,
800 struct apm_softc
*sc
= device_lookup_private(&apmdev_cd
, APMUNIT(dev
));
803 DPRINTF(APMDEBUG_DEVICE
,
804 ("apmclose: pid %d flag %x mode %x\n", l
->l_proc
->p_pid
, flag
, mode
));
809 sc
->sc_flags
&= ~SCFLAG_OWRITE
;
812 sc
->sc_flags
&= ~SCFLAG_OREAD
;
815 if ((sc
->sc_flags
& SCFLAG_OPEN
) == 0) {
816 sc
->sc_event_count
= 0;
817 sc
->sc_event_ptr
= 0;
824 apmdevioctl(dev_t dev
, u_long cmd
, void *data
, int flag
,
827 struct apm_softc
*sc
= device_lookup_private(&apmdev_cd
, APMUNIT(dev
));
828 struct apm_power_info
*powerp
;
829 struct apm_event_info
*evp
;
831 struct apm_ctl
*actl
;
838 case APM_IOC_STANDBY
:
839 if (!apm_do_standby
) {
844 if ((flag
& FWRITE
) == 0) {
851 case APM_IOC_SUSPEND
:
852 if ((flag
& FWRITE
) == 0) {
859 case APM_IOC_NEXTEVENT
:
860 if (!sc
->sc_event_count
)
863 evp
= (struct apm_event_info
*)data
;
864 i
= sc
->sc_event_ptr
+ APM_NEVENTS
- sc
->sc_event_count
;
866 *evp
= sc
->sc_event_list
[i
];
867 sc
->sc_event_count
--;
871 case OAPM_IOC_GETPOWER
:
872 case APM_IOC_GETPOWER
:
873 powerp
= (struct apm_power_info
*)data
;
874 if ((error
= (*sc
->sc_ops
->aa_get_powstat
)(sc
->sc_cookie
, 0,
876 apm_perror("ioctl get power status", error
);
880 switch (apm_minver
) {
885 batt_flags
= powerp
->battery_flags
;
886 powerp
->battery_state
= APM_BATT_UNKNOWN
;
887 if (batt_flags
& APM_BATT_FLAG_HIGH
)
888 powerp
->battery_state
= APM_BATT_HIGH
;
889 else if (batt_flags
& APM_BATT_FLAG_LOW
)
890 powerp
->battery_state
= APM_BATT_LOW
;
891 else if (batt_flags
& APM_BATT_FLAG_CRITICAL
)
892 powerp
->battery_state
= APM_BATT_CRITICAL
;
893 else if (batt_flags
& APM_BATT_FLAG_CHARGING
)
894 powerp
->battery_state
= APM_BATT_CHARGING
;
895 else if (batt_flags
& APM_BATT_FLAG_NO_SYSTEM_BATTERY
)
896 powerp
->battery_state
= APM_BATT_ABSENT
;
910 apmdevpoll(dev_t dev
, int events
, struct lwp
*l
)
912 struct apm_softc
*sc
= device_lookup_private(&apmdev_cd
, APMUNIT(dev
));
916 if (events
& (POLLIN
| POLLRDNORM
)) {
917 if (sc
->sc_event_count
)
918 revents
|= events
& (POLLIN
| POLLRDNORM
);
920 selrecord(l
, &sc
->sc_rsel
);
928 filt_apmrdetach(struct knote
*kn
)
930 struct apm_softc
*sc
= kn
->kn_hook
;
933 SLIST_REMOVE(&sc
->sc_rsel
.sel_klist
, kn
, knote
, kn_selnext
);
938 filt_apmread(struct knote
*kn
, long hint
)
940 struct apm_softc
*sc
= kn
->kn_hook
;
942 kn
->kn_data
= sc
->sc_event_count
;
943 return (kn
->kn_data
> 0);
946 static const struct filterops apmread_filtops
=
947 { 1, NULL
, filt_apmrdetach
, filt_apmread
};
950 apmdevkqfilter(dev_t dev
, struct knote
*kn
)
952 struct apm_softc
*sc
= device_lookup_private(&apmdev_cd
, APMUNIT(dev
));
955 switch (kn
->kn_filter
) {
957 klist
= &sc
->sc_rsel
.sel_klist
;
958 kn
->kn_fop
= &apmread_filtops
;
968 SLIST_INSERT_HEAD(klist
, kn
, kn_selnext
);