1*12ae65d9Sthorpej /* $NetBSD: apmdev.c,v 1.34 2021/09/26 01:16:08 thorpej Exp $ */
2fc3043bcSuch
3fc3043bcSuch /*-
4fc3043bcSuch * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
5fc3043bcSuch * All rights reserved.
6fc3043bcSuch *
7fc3043bcSuch * This code is derived from software contributed to The NetBSD Foundation
8fc3043bcSuch * by John Kohl and Christopher G. Demetriou.
9fc3043bcSuch *
10fc3043bcSuch * Redistribution and use in source and binary forms, with or without
11fc3043bcSuch * modification, are permitted provided that the following conditions
12fc3043bcSuch * are met:
13fc3043bcSuch * 1. Redistributions of source code must retain the above copyright
14fc3043bcSuch * notice, this list of conditions and the following disclaimer.
15fc3043bcSuch * 2. Redistributions in binary form must reproduce the above copyright
16fc3043bcSuch * notice, this list of conditions and the following disclaimer in the
17fc3043bcSuch * documentation and/or other materials provided with the distribution.
18fc3043bcSuch *
19fc3043bcSuch * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20fc3043bcSuch * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21fc3043bcSuch * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22fc3043bcSuch * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23fc3043bcSuch * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24fc3043bcSuch * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25fc3043bcSuch * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26fc3043bcSuch * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27fc3043bcSuch * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28fc3043bcSuch * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29fc3043bcSuch * POSSIBILITY OF SUCH DAMAGE.
30fc3043bcSuch */
31fc3043bcSuch /*
32fc3043bcSuch * from: sys/arch/i386/i386/apm.c,v 1.49 2000/05/08
33fc3043bcSuch */
34fc3043bcSuch
35fc3043bcSuch #include <sys/cdefs.h>
36*12ae65d9Sthorpej __KERNEL_RCSID(0, "$NetBSD: apmdev.c,v 1.34 2021/09/26 01:16:08 thorpej Exp $");
37fc3043bcSuch
389eddf385Speter #ifdef _KERNEL_OPT
3930f9f983Suwe #include "opt_apm.h"
409eddf385Speter #endif
41fc3043bcSuch
42fc3043bcSuch #if defined(DEBUG) && !defined(APMDEBUG)
43fc3043bcSuch #define APMDEBUG
44fc3043bcSuch #endif
45fc3043bcSuch
46fc3043bcSuch #include <sys/param.h>
47fc3043bcSuch #include <sys/systm.h>
48fc3043bcSuch #include <sys/signalvar.h>
49fc3043bcSuch #include <sys/kernel.h>
50fc3043bcSuch #include <sys/proc.h>
51fc3043bcSuch #include <sys/kthread.h>
52fc3043bcSuch #include <sys/malloc.h>
53fc3043bcSuch #include <sys/device.h>
54fc3043bcSuch #include <sys/fcntl.h>
55fc3043bcSuch #include <sys/ioctl.h>
56fc3043bcSuch #include <sys/select.h>
57fc3043bcSuch #include <sys/poll.h>
58fc3043bcSuch #include <sys/conf.h>
59fc3043bcSuch
60fc3043bcSuch #include <dev/hpc/apm/apmvar.h>
61fc3043bcSuch
621cfa6cdcSuwe #ifdef APMDEBUG
63fc3043bcSuch #define DPRINTF(f, x) do { if (apmdebug & (f)) printf x; } while (0)
64fc3043bcSuch
65fc3043bcSuch
66fc3043bcSuch #ifdef APMDEBUG_VALUE
67fc3043bcSuch int apmdebug = APMDEBUG_VALUE;
68fc3043bcSuch #else
69fc3043bcSuch int apmdebug = 0;
701cfa6cdcSuwe #endif /* APMDEBUG_VALUE */
711cfa6cdcSuwe
72fc3043bcSuch #else
73fc3043bcSuch #define DPRINTF(f, x) /**/
741cfa6cdcSuwe #endif /* APMDEBUG */
75fc3043bcSuch
76fc3043bcSuch #define SCFLAG_OREAD 0x0000001
77fc3043bcSuch #define SCFLAG_OWRITE 0x0000002
78fc3043bcSuch #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
79fc3043bcSuch
80fc3043bcSuch #define APMUNIT(dev) (minor(dev)&0xf0)
817cbde2c0Suwe #define APM(dev) (minor(dev)&0x0f)
827cbde2c0Suwe #define APM_NORMAL 0
837cbde2c0Suwe #define APM_CTL 8
84fc3043bcSuch
85fc3043bcSuch /*
86fc3043bcSuch * A brief note on the locking protocol: it's very simple; we
87fc3043bcSuch * assert an exclusive lock any time thread context enters the
88fc3043bcSuch * APM module. This is both the APM thread itself, as well as
89fc3043bcSuch * user context.
90fc3043bcSuch */
911cfa6cdcSuwe #define APM_LOCK(apmsc) \
921cfa6cdcSuwe (void) mutex_enter(&(apmsc)->sc_lock)
931cfa6cdcSuwe #define APM_UNLOCK(apmsc) \
941cfa6cdcSuwe (void) mutex_exit(&(apmsc)->sc_lock)
95fc3043bcSuch
967cbde2c0Suwe static void apmdevattach(device_t, device_t, void *);
977cbde2c0Suwe static int apmdevmatch(device_t, cfdata_t, void *);
98fc3043bcSuch
99fc3043bcSuch static void apm_event_handle(struct apm_softc *, u_int, u_int);
100fc3043bcSuch static void apm_periodic_check(struct apm_softc *);
101fc3043bcSuch static void apm_thread(void *);
102fc3043bcSuch static void apm_perror(const char *, int, ...)
103fc3043bcSuch __attribute__((__format__(__printf__,1,3)));
104fc3043bcSuch #ifdef APM_POWER_PRINT
105fc3043bcSuch static void apm_power_print(struct apm_softc *, struct apm_power_info *);
106fc3043bcSuch #endif
107fc3043bcSuch static int apm_record_event(struct apm_softc *, u_int);
1081cfa6cdcSuwe static void apm_set_ver(struct apm_softc *);
109fc3043bcSuch static void apm_standby(struct apm_softc *);
110fc3043bcSuch static void apm_suspend(struct apm_softc *);
111fc3043bcSuch static void apm_resume(struct apm_softc *, u_int, u_int);
112fc3043bcSuch
1131cfa6cdcSuwe CFATTACH_DECL_NEW(apmdev, sizeof(struct apm_softc),
1147cbde2c0Suwe apmdevmatch, apmdevattach, NULL, NULL);
115fc3043bcSuch
116fc3043bcSuch extern struct cfdriver apmdev_cd;
117fc3043bcSuch
118fc3043bcSuch dev_type_open(apmdevopen);
119fc3043bcSuch dev_type_close(apmdevclose);
120fc3043bcSuch dev_type_ioctl(apmdevioctl);
121fc3043bcSuch dev_type_poll(apmdevpoll);
122fc3043bcSuch dev_type_kqfilter(apmdevkqfilter);
123fc3043bcSuch
124fc3043bcSuch const struct cdevsw apmdev_cdevsw = {
125a68f9396Sdholland .d_open = apmdevopen,
126a68f9396Sdholland .d_close = apmdevclose,
127a68f9396Sdholland .d_read = noread,
128a68f9396Sdholland .d_write = nowrite,
129a68f9396Sdholland .d_ioctl = apmdevioctl,
130a68f9396Sdholland .d_stop = nostop,
131a68f9396Sdholland .d_tty = notty,
132a68f9396Sdholland .d_poll = apmdevpoll,
133a68f9396Sdholland .d_mmap = nommap,
134a68f9396Sdholland .d_kqfilter = apmdevkqfilter,
135f9228f42Sdholland .d_discard = nodiscard,
136a68f9396Sdholland .d_flag = D_OTHER
137fc3043bcSuch };
138fc3043bcSuch
139fc3043bcSuch /* configurable variables */
140fc3043bcSuch #ifdef APM_NO_STANDBY
141fc3043bcSuch int apm_do_standby = 0;
142fc3043bcSuch #else
143fc3043bcSuch int apm_do_standby = 1;
144fc3043bcSuch #endif
145fc3043bcSuch #ifdef APM_V10_ONLY
146fc3043bcSuch int apm_v11_enabled = 0;
147fc3043bcSuch #else
148fc3043bcSuch int apm_v11_enabled = 1;
149fc3043bcSuch #endif
150fc3043bcSuch #ifdef APM_NO_V12
151fc3043bcSuch int apm_v12_enabled = 0;
152fc3043bcSuch #else
153fc3043bcSuch int apm_v12_enabled = 1;
154fc3043bcSuch #endif
155fc3043bcSuch
156fc3043bcSuch /* variables used during operation (XXX cgd) */
157fc3043bcSuch u_char apm_majver, apm_minver;
158fc3043bcSuch int apm_inited;
159fc3043bcSuch int apm_standbys, apm_userstandbys, apm_suspends, apm_battlow;
160fc3043bcSuch int apm_damn_fool_bios, apm_op_inprog;
161fc3043bcSuch int apm_evindex;
162fc3043bcSuch
163fc3043bcSuch static int apm_spl; /* saved spl while suspended */
164fc3043bcSuch
1657cbde2c0Suwe const char *
apm_strerror(int code)166fc3043bcSuch apm_strerror(int code)
167fc3043bcSuch {
168fc3043bcSuch switch (code) {
169fc3043bcSuch case APM_ERR_PM_DISABLED:
170fc3043bcSuch return ("power management disabled");
171fc3043bcSuch case APM_ERR_REALALREADY:
172fc3043bcSuch return ("real mode interface already connected");
173fc3043bcSuch case APM_ERR_NOTCONN:
174fc3043bcSuch return ("interface not connected");
175fc3043bcSuch case APM_ERR_16ALREADY:
176fc3043bcSuch return ("16-bit interface already connected");
177fc3043bcSuch case APM_ERR_16NOTSUPP:
178fc3043bcSuch return ("16-bit interface not supported");
179fc3043bcSuch case APM_ERR_32ALREADY:
180fc3043bcSuch return ("32-bit interface already connected");
181fc3043bcSuch case APM_ERR_32NOTSUPP:
182fc3043bcSuch return ("32-bit interface not supported");
183fc3043bcSuch case APM_ERR_UNRECOG_DEV:
184fc3043bcSuch return ("unrecognized device ID");
185fc3043bcSuch case APM_ERR_ERANGE:
186fc3043bcSuch return ("parameter out of range");
187fc3043bcSuch case APM_ERR_NOTENGAGED:
188fc3043bcSuch return ("interface not engaged");
189fc3043bcSuch case APM_ERR_UNABLE:
190fc3043bcSuch return ("unable to enter requested state");
191fc3043bcSuch case APM_ERR_NOEVENTS:
192fc3043bcSuch return ("no pending events");
193fc3043bcSuch case APM_ERR_NOT_PRESENT:
194fc3043bcSuch return ("no APM present");
195fc3043bcSuch default:
196fc3043bcSuch return ("unknown error code");
197fc3043bcSuch }
198fc3043bcSuch }
199fc3043bcSuch
200fc3043bcSuch static void
apm_perror(const char * str,int errinfo,...)201fc3043bcSuch apm_perror(const char *str, int errinfo, ...) /* XXX cgd */
202fc3043bcSuch {
203fc3043bcSuch va_list ap;
204fc3043bcSuch
205fc3043bcSuch printf("APM ");
206fc3043bcSuch
207fc3043bcSuch va_start(ap, errinfo);
208fc3043bcSuch vprintf(str, ap); /* XXX cgd */
209fc3043bcSuch va_end(ap);
210fc3043bcSuch
211fc3043bcSuch printf(": %s\n", apm_strerror(errinfo));
212fc3043bcSuch }
213fc3043bcSuch
214fc3043bcSuch #ifdef APM_POWER_PRINT
215fc3043bcSuch static void
apm_power_print(struct apm_softc * sc,struct apm_power_info * pi)216fc3043bcSuch apm_power_print(struct apm_softc *sc, struct apm_power_info *pi)
217fc3043bcSuch {
218fc3043bcSuch
219fc3043bcSuch if (pi->battery_life != APM_BATT_LIFE_UNKNOWN) {
2201cfa6cdcSuwe aprint_normal_dev(sc->sc_dev,
2211cfa6cdcSuwe "battery life expectancy: %d%%\n",
2221cfa6cdcSuwe pi->battery_life);
223fc3043bcSuch }
2241cfa6cdcSuwe aprint_normal_dev(sc->sc_dev, "A/C state: ");
225fc3043bcSuch switch (pi->ac_state) {
226fc3043bcSuch case APM_AC_OFF:
227fc3043bcSuch printf("off\n");
228fc3043bcSuch break;
229fc3043bcSuch case APM_AC_ON:
230fc3043bcSuch printf("on\n");
231fc3043bcSuch break;
232fc3043bcSuch case APM_AC_BACKUP:
233fc3043bcSuch printf("backup power\n");
234fc3043bcSuch break;
235fc3043bcSuch default:
236fc3043bcSuch case APM_AC_UNKNOWN:
237fc3043bcSuch printf("unknown\n");
238fc3043bcSuch break;
239fc3043bcSuch }
2401cfa6cdcSuwe aprint_normal_dev(sc->sc_dev, "battery charge state:");
2411cfa6cdcSuwe if (apm_minver == 0)
242fc3043bcSuch switch (pi->battery_state) {
243fc3043bcSuch case APM_BATT_HIGH:
244fc3043bcSuch printf("high\n");
245fc3043bcSuch break;
246fc3043bcSuch case APM_BATT_LOW:
247fc3043bcSuch printf("low\n");
248fc3043bcSuch break;
249fc3043bcSuch case APM_BATT_CRITICAL:
250fc3043bcSuch printf("critical\n");
251fc3043bcSuch break;
252fc3043bcSuch case APM_BATT_CHARGING:
253fc3043bcSuch printf("charging\n");
254fc3043bcSuch break;
255fc3043bcSuch case APM_BATT_UNKNOWN:
256fc3043bcSuch printf("unknown\n");
257fc3043bcSuch break;
258fc3043bcSuch default:
259fc3043bcSuch printf("undecoded state %x\n", pi->battery_state);
260fc3043bcSuch break;
261fc3043bcSuch }
2621cfa6cdcSuwe else if (apm_minver >= 1) {
2631cfa6cdcSuwe if (pi->battery_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
2641cfa6cdcSuwe printf(" no battery");
2651cfa6cdcSuwe else {
2661cfa6cdcSuwe if (pi->battery_flags & APM_BATT_FLAG_HIGH)
2671cfa6cdcSuwe printf(" high");
2681cfa6cdcSuwe if (pi->battery_flags & APM_BATT_FLAG_LOW)
2691cfa6cdcSuwe printf(" low");
2701cfa6cdcSuwe if (pi->battery_flags & APM_BATT_FLAG_CRITICAL)
2711cfa6cdcSuwe printf(" critical");
2721cfa6cdcSuwe if (pi->battery_flags & APM_BATT_FLAG_CHARGING)
273fc3043bcSuch printf(" charging");
274fc3043bcSuch }
2751cfa6cdcSuwe printf("\n");
2761cfa6cdcSuwe if (pi->minutes_valid) {
2771cfa6cdcSuwe aprint_normal_dev(sc->sc_dev, "estimated ");
2781cfa6cdcSuwe if (pi->minutes_left / 60)
279fc3043bcSuch printf("%dh ", pi->minutes_left / 60);
2801cfa6cdcSuwe printf("%dm\n", pi->minutes_left % 60);
2811cfa6cdcSuwe }
282fc3043bcSuch }
283fc3043bcSuch return;
284fc3043bcSuch }
285fc3043bcSuch #endif
286fc3043bcSuch
287fc3043bcSuch static void
apm_suspend(struct apm_softc * sc)288fc3043bcSuch apm_suspend(struct apm_softc *sc)
289fc3043bcSuch {
2901cfa6cdcSuwe int error;
291fc3043bcSuch
292fc3043bcSuch if (sc->sc_power_state == PWR_SUSPEND) {
293fc3043bcSuch #ifdef APMDEBUG
2941cfa6cdcSuwe aprint_debug_dev(sc->sc_dev,
2951cfa6cdcSuwe "apm_suspend: already suspended?\n");
296fc3043bcSuch #endif
297fc3043bcSuch return;
298fc3043bcSuch }
299fc3043bcSuch sc->sc_power_state = PWR_SUSPEND;
300fc3043bcSuch
301fc3043bcSuch dopowerhooks(PWR_SOFTSUSPEND);
302fc3043bcSuch (void) tsleep(sc, PWAIT, "apmsuspend", hz/2);
303fc3043bcSuch
304fc3043bcSuch apm_spl = splhigh();
305fc3043bcSuch
306fc3043bcSuch dopowerhooks(PWR_SUSPEND);
307fc3043bcSuch
3081cfa6cdcSuwe error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
3091cfa6cdcSuwe APM_SYS_SUSPEND);
3107cbde2c0Suwe
3117cbde2c0Suwe if (error)
3127cbde2c0Suwe apm_resume(sc, 0, 0);
313fc3043bcSuch }
314fc3043bcSuch
315fc3043bcSuch static void
apm_standby(struct apm_softc * sc)316fc3043bcSuch apm_standby(struct apm_softc *sc)
317fc3043bcSuch {
3181cfa6cdcSuwe int error;
319fc3043bcSuch
320fc3043bcSuch if (sc->sc_power_state == PWR_STANDBY) {
321fc3043bcSuch #ifdef APMDEBUG
3221cfa6cdcSuwe aprint_debug_dev(sc->sc_dev,
3231cfa6cdcSuwe "apm_standby: already standing by?\n");
324fc3043bcSuch #endif
325fc3043bcSuch return;
326fc3043bcSuch }
327fc3043bcSuch sc->sc_power_state = PWR_STANDBY;
328fc3043bcSuch
329fc3043bcSuch dopowerhooks(PWR_SOFTSTANDBY);
330fc3043bcSuch (void) tsleep(sc, PWAIT, "apmstandby", hz/2);
331fc3043bcSuch
332fc3043bcSuch apm_spl = splhigh();
333fc3043bcSuch
334fc3043bcSuch dopowerhooks(PWR_STANDBY);
3351cfa6cdcSuwe
3361cfa6cdcSuwe error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
3371cfa6cdcSuwe APM_SYS_STANDBY);
3387cbde2c0Suwe if (error)
3397cbde2c0Suwe apm_resume(sc, 0, 0);
340fc3043bcSuch }
341fc3043bcSuch
342fc3043bcSuch static void
apm_resume(struct apm_softc * sc,u_int event_type,u_int event_info)343168cd830Schristos apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info)
344fc3043bcSuch {
345fc3043bcSuch
346fc3043bcSuch if (sc->sc_power_state == PWR_RESUME) {
347fc3043bcSuch #ifdef APMDEBUG
3481cfa6cdcSuwe aprint_debug_dev(sc->sc_dev, "apm_resume: already running?\n");
349fc3043bcSuch #endif
350fc3043bcSuch return;
351fc3043bcSuch }
352fc3043bcSuch sc->sc_power_state = PWR_RESUME;
353fc3043bcSuch
3541cfa6cdcSuwe #if 0 /* XXX: def TIME_FREQ */
355fc3043bcSuch /*
356fc3043bcSuch * Some system requires its clock to be initialized after hybernation.
357fc3043bcSuch */
3581cfa6cdcSuwe initrtclock(TIMER_FREQ);
3591cfa6cdcSuwe #endif
360fc3043bcSuch
36150d22dfbSgdamore inittodr(time_second);
362fc3043bcSuch dopowerhooks(PWR_RESUME);
363fc3043bcSuch
364fc3043bcSuch splx(apm_spl);
365fc3043bcSuch
366fc3043bcSuch dopowerhooks(PWR_SOFTRESUME);
367fc3043bcSuch
368fc3043bcSuch apm_record_event(sc, event_type);
369fc3043bcSuch }
370fc3043bcSuch
371fc3043bcSuch /*
372fc3043bcSuch * return 0 if the user will notice and handle the event,
373fc3043bcSuch * return 1 if the kernel driver should do so.
374fc3043bcSuch */
375fc3043bcSuch static int
apm_record_event(struct apm_softc * sc,u_int event_type)376fc3043bcSuch apm_record_event(struct apm_softc *sc, u_int event_type)
377fc3043bcSuch {
378fc3043bcSuch struct apm_event_info *evp;
379fc3043bcSuch
380fc3043bcSuch if ((sc->sc_flags & SCFLAG_OPEN) == 0)
381fc3043bcSuch return 1; /* no user waiting */
3821cfa6cdcSuwe if (sc->sc_event_count == APM_NEVENTS)
383fc3043bcSuch return 1; /* overflow */
3841cfa6cdcSuwe evp = &sc->sc_event_list[sc->sc_event_ptr];
3851cfa6cdcSuwe sc->sc_event_count++;
3861cfa6cdcSuwe sc->sc_event_ptr++;
3871cfa6cdcSuwe sc->sc_event_ptr %= APM_NEVENTS;
388fc3043bcSuch evp->type = event_type;
389fc3043bcSuch evp->index = ++apm_evindex;
390c6186facSrmind selnotify(&sc->sc_rsel, 0, 0);
391fc3043bcSuch return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */
392fc3043bcSuch }
393fc3043bcSuch
394fc3043bcSuch static void
apm_event_handle(struct apm_softc * sc,u_int event_code,u_int event_info)395fc3043bcSuch apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info)
396fc3043bcSuch {
397fc3043bcSuch int error;
3986a99517aSuwe const char *code;
399fc3043bcSuch struct apm_power_info pi;
400fc3043bcSuch
401fc3043bcSuch switch (event_code) {
402fc3043bcSuch case APM_USER_STANDBY_REQ:
403fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n"));
404fc3043bcSuch if (apm_do_standby) {
4057cbde2c0Suwe if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
406fc3043bcSuch apm_userstandbys++;
407fc3043bcSuch apm_op_inprog++;
4081cfa6cdcSuwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
4091cfa6cdcSuwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
410fc3043bcSuch } else {
4111cfa6cdcSuwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
4121cfa6cdcSuwe APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
413fc3043bcSuch /* in case BIOS hates being spurned */
4141cfa6cdcSuwe (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
415fc3043bcSuch }
416fc3043bcSuch break;
417fc3043bcSuch
418fc3043bcSuch case APM_STANDBY_REQ:
419fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n"));
420fc3043bcSuch if (apm_standbys || apm_suspends) {
421fc3043bcSuch DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
422fc3043bcSuch ("damn fool BIOS did not wait for answer\n"));
423fc3043bcSuch /* just give up the fight */
424fc3043bcSuch apm_damn_fool_bios = 1;
425fc3043bcSuch }
426fc3043bcSuch if (apm_do_standby) {
4277cbde2c0Suwe if (apm_op_inprog == 0 &&
4287cbde2c0Suwe apm_record_event(sc, event_code))
429fc3043bcSuch apm_standbys++;
430fc3043bcSuch apm_op_inprog++;
4311cfa6cdcSuwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
4321cfa6cdcSuwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
433fc3043bcSuch } else {
4341cfa6cdcSuwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
4351cfa6cdcSuwe APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
436fc3043bcSuch /* in case BIOS hates being spurned */
4371cfa6cdcSuwe (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
438fc3043bcSuch }
439fc3043bcSuch break;
440fc3043bcSuch
441fc3043bcSuch case APM_USER_SUSPEND_REQ:
442fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: user suspend request\n"));
4437cbde2c0Suwe if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
444fc3043bcSuch apm_suspends++;
445fc3043bcSuch apm_op_inprog++;
4461cfa6cdcSuwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
4471cfa6cdcSuwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
448fc3043bcSuch break;
449fc3043bcSuch
450fc3043bcSuch case APM_SUSPEND_REQ:
451fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: system suspend request\n"));
452fc3043bcSuch if (apm_standbys || apm_suspends) {
453fc3043bcSuch DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
454fc3043bcSuch ("damn fool BIOS did not wait for answer\n"));
455fc3043bcSuch /* just give up the fight */
456fc3043bcSuch apm_damn_fool_bios = 1;
457fc3043bcSuch }
4587cbde2c0Suwe if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
459fc3043bcSuch apm_suspends++;
460fc3043bcSuch apm_op_inprog++;
4611cfa6cdcSuwe (void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
4621cfa6cdcSuwe APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
463fc3043bcSuch break;
464fc3043bcSuch
465fc3043bcSuch case APM_POWER_CHANGE:
466fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: power status change\n"));
4679a25b0cbSuwe error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
468fc3043bcSuch #ifdef APM_POWER_PRINT
469fc3043bcSuch /* only print if nobody is catching events. */
470fc3043bcSuch if (error == 0 &&
471fc3043bcSuch (sc->sc_flags & (SCFLAG_OREAD|SCFLAG_OWRITE)) == 0)
472fc3043bcSuch apm_power_print(sc, &pi);
47396aaef2dSchristos #else
47496aaef2dSchristos __USE(error);
475fc3043bcSuch #endif
476fc3043bcSuch apm_record_event(sc, event_code);
477fc3043bcSuch break;
478fc3043bcSuch
479fc3043bcSuch case APM_NORMAL_RESUME:
480fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: resume system\n"));
481fc3043bcSuch apm_resume(sc, event_code, event_info);
482fc3043bcSuch break;
483fc3043bcSuch
484fc3043bcSuch case APM_CRIT_RESUME:
485fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: critical resume system"));
486fc3043bcSuch apm_resume(sc, event_code, event_info);
487fc3043bcSuch break;
488fc3043bcSuch
489fc3043bcSuch case APM_SYS_STANDBY_RESUME:
490fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby resume\n"));
491fc3043bcSuch apm_resume(sc, event_code, event_info);
492fc3043bcSuch break;
493fc3043bcSuch
494fc3043bcSuch case APM_UPDATE_TIME:
495fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: update time\n"));
496fc3043bcSuch apm_resume(sc, event_code, event_info);
497fc3043bcSuch break;
498fc3043bcSuch
499fc3043bcSuch case APM_CRIT_SUSPEND_REQ:
500fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: critical system suspend\n"));
501fc3043bcSuch apm_record_event(sc, event_code);
502fc3043bcSuch apm_suspend(sc);
503fc3043bcSuch break;
504fc3043bcSuch
505fc3043bcSuch case APM_BATTERY_LOW:
506fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: battery low\n"));
507fc3043bcSuch apm_battlow++;
508fc3043bcSuch apm_record_event(sc, event_code);
509fc3043bcSuch break;
510fc3043bcSuch
511fc3043bcSuch case APM_CAP_CHANGE:
512fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apmev: capability change\n"));
513fc3043bcSuch if (apm_minver < 2) {
514fc3043bcSuch DPRINTF(APMDEBUG_EVENTS, ("apm: unexpected event\n"));
515fc3043bcSuch } else {
516fc3043bcSuch u_int numbatts, capflags;
5171cfa6cdcSuwe (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie,
518fc3043bcSuch &numbatts, &capflags);
5199a25b0cbSuwe (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
520fc3043bcSuch }
521fc3043bcSuch break;
522fc3043bcSuch
523fc3043bcSuch default:
524fc3043bcSuch switch (event_code >> 8) {
525fc3043bcSuch case 0:
526fc3043bcSuch code = "reserved system";
527fc3043bcSuch break;
528fc3043bcSuch case 1:
529fc3043bcSuch code = "reserved device";
530fc3043bcSuch break;
531fc3043bcSuch case 2:
532fc3043bcSuch code = "OEM defined";
533fc3043bcSuch break;
534fc3043bcSuch default:
535fc3043bcSuch code = "reserved";
536fc3043bcSuch break;
537fc3043bcSuch }
538fc3043bcSuch printf("APM: %s event code %x\n", code, event_code);
539fc3043bcSuch }
540fc3043bcSuch }
541fc3043bcSuch
542fc3043bcSuch static void
apm_periodic_check(struct apm_softc * sc)543fc3043bcSuch apm_periodic_check(struct apm_softc *sc)
544fc3043bcSuch {
545fc3043bcSuch int error;
546fc3043bcSuch u_int event_code, event_info;
547fc3043bcSuch
548fc3043bcSuch
549fc3043bcSuch /*
550fc3043bcSuch * tell the BIOS we're working on it, if asked to do a
551fc3043bcSuch * suspend/standby
552fc3043bcSuch */
553fc3043bcSuch if (apm_op_inprog)
5541cfa6cdcSuwe (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
555fc3043bcSuch APM_LASTREQ_INPROG);
556fc3043bcSuch
5571cfa6cdcSuwe while ((error = (*sc->sc_ops->aa_get_event)(sc->sc_cookie, &event_code,
5581cfa6cdcSuwe &event_info)) == 0 && !apm_damn_fool_bios)
559fc3043bcSuch apm_event_handle(sc, event_code, event_info);
560fc3043bcSuch
561fc3043bcSuch if (error != APM_ERR_NOEVENTS)
562fc3043bcSuch apm_perror("get event", error);
563fc3043bcSuch if (apm_suspends) {
564fc3043bcSuch apm_op_inprog = 0;
565fc3043bcSuch apm_suspend(sc);
566fc3043bcSuch } else if (apm_standbys || apm_userstandbys) {
567fc3043bcSuch apm_op_inprog = 0;
568fc3043bcSuch apm_standby(sc);
569fc3043bcSuch }
570fc3043bcSuch apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
571fc3043bcSuch apm_damn_fool_bios = 0;
572fc3043bcSuch }
573fc3043bcSuch
574fc3043bcSuch static void
apm_set_ver(struct apm_softc * sc)5751cfa6cdcSuwe apm_set_ver(struct apm_softc *sc)
576fc3043bcSuch {
577fc3043bcSuch
578fc3043bcSuch if (apm_v12_enabled &&
5791cfa6cdcSuwe APM_MAJOR_VERS(sc->sc_vers) == 1 &&
5801cfa6cdcSuwe APM_MINOR_VERS(sc->sc_vers) == 2) {
581fc3043bcSuch apm_majver = 1;
582fc3043bcSuch apm_minver = 2;
583fc3043bcSuch goto ok;
584fc3043bcSuch }
585fc3043bcSuch
586fc3043bcSuch if (apm_v11_enabled &&
5871cfa6cdcSuwe APM_MAJOR_VERS(sc->sc_vers) == 1 &&
5881cfa6cdcSuwe APM_MINOR_VERS(sc->sc_vers) == 1) {
589fc3043bcSuch apm_majver = 1;
590fc3043bcSuch apm_minver = 1;
591fc3043bcSuch } else {
592fc3043bcSuch apm_majver = 1;
593fc3043bcSuch apm_minver = 0;
594fc3043bcSuch }
595fc3043bcSuch ok:
5961cfa6cdcSuwe aprint_normal("Power Management spec V%d.%d", apm_majver, apm_minver);
597fc3043bcSuch apm_inited = 1;
598fc3043bcSuch }
599fc3043bcSuch
600fc3043bcSuch static int
apmdevmatch(device_t parent,cfdata_t match,void * aux)6017cbde2c0Suwe apmdevmatch(device_t parent, cfdata_t match, void *aux)
602fc3043bcSuch {
6037cbde2c0Suwe
6047cbde2c0Suwe return apm_match();
605fc3043bcSuch }
606fc3043bcSuch
607fc3043bcSuch static void
apmdevattach(device_t parent,device_t self,void * aux)6087cbde2c0Suwe apmdevattach(device_t parent, device_t self, void *aux)
609fc3043bcSuch {
6101cfa6cdcSuwe struct apm_softc *sc;
611fc3043bcSuch struct apmdev_attach_args *aaa = aux;
612fc3043bcSuch
6131cfa6cdcSuwe sc = device_private(self);
6141cfa6cdcSuwe sc->sc_dev = self;
615fc3043bcSuch
6161cfa6cdcSuwe sc->sc_detail = aaa->apm_detail;
6171cfa6cdcSuwe sc->sc_vers = aaa->apm_detail & 0xffff; /* XXX: magic */
618fc3043bcSuch
6191cfa6cdcSuwe sc->sc_ops = aaa->accessops;
6201cfa6cdcSuwe sc->sc_cookie = aaa->accesscookie;
6211cfa6cdcSuwe
6227cbde2c0Suwe apm_attach(sc);
6237cbde2c0Suwe }
6247cbde2c0Suwe
6257cbde2c0Suwe /*
6267cbde2c0Suwe * Print function (for parent devices).
6277cbde2c0Suwe */
6287cbde2c0Suwe int
apmprint(void * aux,const char * pnp)6297cbde2c0Suwe apmprint(void *aux, const char *pnp)
6307cbde2c0Suwe {
6317cbde2c0Suwe if (pnp)
6327cbde2c0Suwe aprint_normal("apm at %s", pnp);
6337cbde2c0Suwe
6347cbde2c0Suwe return (UNCONF);
6357cbde2c0Suwe }
6367cbde2c0Suwe
6377cbde2c0Suwe int
apm_match(void)6387cbde2c0Suwe apm_match(void)
6397cbde2c0Suwe {
6407cbde2c0Suwe static int got;
6417cbde2c0Suwe return !got++;
6427cbde2c0Suwe }
6437cbde2c0Suwe
6447cbde2c0Suwe void
apm_attach(struct apm_softc * sc)6457cbde2c0Suwe apm_attach(struct apm_softc *sc)
6467cbde2c0Suwe {
6477cbde2c0Suwe struct apm_power_info pinfo;
6487cbde2c0Suwe u_int numbatts, capflags;
6497cbde2c0Suwe int error;
6507cbde2c0Suwe
6511cfa6cdcSuwe aprint_naive("\n");
6521cfa6cdcSuwe aprint_normal(": ");
6531cfa6cdcSuwe
6541cfa6cdcSuwe switch ((APM_MAJOR_VERS(sc->sc_vers) << 8) + APM_MINOR_VERS(sc->sc_vers)) {
655fc3043bcSuch case 0x0100:
656fc3043bcSuch apm_v11_enabled = 0;
657fc3043bcSuch apm_v12_enabled = 0;
658fc3043bcSuch break;
659fc3043bcSuch case 0x0101:
660fc3043bcSuch apm_v12_enabled = 0;
661fc3043bcSuch /* fall through */
662fc3043bcSuch case 0x0102:
663fc3043bcSuch default:
664fc3043bcSuch break;
665fc3043bcSuch }
666fc3043bcSuch
6671cfa6cdcSuwe apm_set_ver(sc); /* prints version info */
6681cfa6cdcSuwe aprint_normal("\n");
669fc3043bcSuch if (apm_minver >= 2)
6701cfa6cdcSuwe (*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, &numbatts,
6711cfa6cdcSuwe &capflags);
672fc3043bcSuch
673fc3043bcSuch /*
674fc3043bcSuch * enable power management
675fc3043bcSuch */
6761cfa6cdcSuwe (*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
677fc3043bcSuch
6789a25b0cbSuwe error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pinfo);
679fc3043bcSuch if (error == 0) {
680fc3043bcSuch #ifdef APM_POWER_PRINT
6811cfa6cdcSuwe apm_power_print(sc, &pinfo);
682fc3043bcSuch #endif
683fc3043bcSuch } else
684fc3043bcSuch apm_perror("get power status", error);
685fc3043bcSuch
6861cfa6cdcSuwe if (sc->sc_ops->aa_cpu_busy)
6871cfa6cdcSuwe (*sc->sc_ops->aa_cpu_busy)(sc->sc_cookie);
6881cfa6cdcSuwe
6891cfa6cdcSuwe mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
690fc3043bcSuch
691fc3043bcSuch /* Initial state is `resumed'. */
692fc3043bcSuch sc->sc_power_state = PWR_RESUME;
6931cfa6cdcSuwe selinit(&sc->sc_rsel);
6941cfa6cdcSuwe selinit(&sc->sc_xsel);
695fc3043bcSuch
696fc3043bcSuch /* Do an initial check. */
697fc3043bcSuch apm_periodic_check(sc);
698fc3043bcSuch
699fc3043bcSuch /*
700fc3043bcSuch * Create a kernel thread to periodically check for APM events,
701fc3043bcSuch * and notify other subsystems when they occur.
702fc3043bcSuch */
70388ab7da9Sad if (kthread_create(PRI_NONE, 0, NULL, apm_thread, sc,
7041cfa6cdcSuwe &sc->sc_thread, "%s", device_xname(sc->sc_dev)) != 0) {
70588ab7da9Sad /*
70688ab7da9Sad * We were unable to create the APM thread; bail out.
70788ab7da9Sad */
7081cfa6cdcSuwe if (sc->sc_ops->aa_disconnect)
7091cfa6cdcSuwe (*sc->sc_ops->aa_disconnect)(sc->sc_cookie);
7101cfa6cdcSuwe aprint_error_dev(sc->sc_dev, "unable to create thread, "
7110e50a946Scegger "kernel APM support disabled\n");
71288ab7da9Sad }
713fc3043bcSuch }
714fc3043bcSuch
715fc3043bcSuch void
apm_thread(void * arg)716fc3043bcSuch apm_thread(void *arg)
717fc3043bcSuch {
718fc3043bcSuch struct apm_softc *apmsc = arg;
719fc3043bcSuch
720fc3043bcSuch /*
721fc3043bcSuch * Loop forever, doing a periodic check for APM events.
722fc3043bcSuch */
723fc3043bcSuch for (;;) {
724fc3043bcSuch APM_LOCK(apmsc);
725fc3043bcSuch apm_periodic_check(apmsc);
726fc3043bcSuch APM_UNLOCK(apmsc);
727fc3043bcSuch (void) tsleep(apmsc, PWAIT, "apmev", (8 * hz) / 7);
728fc3043bcSuch }
729fc3043bcSuch }
730fc3043bcSuch
731fc3043bcSuch int
apmdevopen(dev_t dev,int flag,int mode,struct lwp * l)732168cd830Schristos apmdevopen(dev_t dev, int flag, int mode, struct lwp *l)
733fc3043bcSuch {
7347cbde2c0Suwe int ctl = APM(dev);
735fc3043bcSuch int error = 0;
736fc3043bcSuch struct apm_softc *sc;
737fc3043bcSuch
738cf990769Srafal sc = device_lookup_private(&apmdev_cd, APMUNIT(dev));
739fc3043bcSuch if (!sc)
740fc3043bcSuch return ENXIO;
741fc3043bcSuch
742fc3043bcSuch if (!apm_inited)
743fc3043bcSuch return ENXIO;
744fc3043bcSuch
745fc3043bcSuch DPRINTF(APMDEBUG_DEVICE,
74695e1ffb1Schristos ("apmopen: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
747fc3043bcSuch
748fc3043bcSuch APM_LOCK(sc);
749fc3043bcSuch switch (ctl) {
7507cbde2c0Suwe case APM_CTL:
751fc3043bcSuch if (!(flag & FWRITE)) {
752fc3043bcSuch error = EINVAL;
753fc3043bcSuch break;
754fc3043bcSuch }
755fc3043bcSuch if (sc->sc_flags & SCFLAG_OWRITE) {
756fc3043bcSuch error = EBUSY;
757fc3043bcSuch break;
758fc3043bcSuch }
759fc3043bcSuch sc->sc_flags |= SCFLAG_OWRITE;
760fc3043bcSuch break;
7617cbde2c0Suwe case APM_NORMAL:
762fc3043bcSuch if (!(flag & FREAD) || (flag & FWRITE)) {
763fc3043bcSuch error = EINVAL;
764fc3043bcSuch break;
765fc3043bcSuch }
766fc3043bcSuch sc->sc_flags |= SCFLAG_OREAD;
767fc3043bcSuch break;
768fc3043bcSuch default:
769fc3043bcSuch error = ENXIO;
770fc3043bcSuch break;
771fc3043bcSuch }
772fc3043bcSuch APM_UNLOCK(sc);
773fc3043bcSuch
774fc3043bcSuch return (error);
775fc3043bcSuch }
776fc3043bcSuch
777fc3043bcSuch int
apmdevclose(dev_t dev,int flag,int mode,struct lwp * l)778168cd830Schristos apmdevclose(dev_t dev, int flag, int mode,
779168cd830Schristos struct lwp *l)
780fc3043bcSuch {
781cf990769Srafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev));
7827cbde2c0Suwe int ctl = APM(dev);
783fc3043bcSuch
784fc3043bcSuch DPRINTF(APMDEBUG_DEVICE,
78595e1ffb1Schristos ("apmclose: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
786fc3043bcSuch
787fc3043bcSuch APM_LOCK(sc);
788fc3043bcSuch switch (ctl) {
7897cbde2c0Suwe case APM_CTL:
790fc3043bcSuch sc->sc_flags &= ~SCFLAG_OWRITE;
791fc3043bcSuch break;
7927cbde2c0Suwe case APM_NORMAL:
793fc3043bcSuch sc->sc_flags &= ~SCFLAG_OREAD;
794fc3043bcSuch break;
795fc3043bcSuch }
796fc3043bcSuch if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
7971cfa6cdcSuwe sc->sc_event_count = 0;
7981cfa6cdcSuwe sc->sc_event_ptr = 0;
799fc3043bcSuch }
800fc3043bcSuch APM_UNLOCK(sc);
801fc3043bcSuch return 0;
802fc3043bcSuch }
803fc3043bcSuch
804fc3043bcSuch int
apmdevioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)80553524e44Schristos apmdevioctl(dev_t dev, u_long cmd, void *data, int flag,
806168cd830Schristos struct lwp *l)
807fc3043bcSuch {
808cf990769Srafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev));
809fc3043bcSuch struct apm_power_info *powerp;
810fc3043bcSuch struct apm_event_info *evp;
811fc3043bcSuch #if 0
812fc3043bcSuch struct apm_ctl *actl;
813fc3043bcSuch #endif
814fc3043bcSuch int i, error = 0;
815fc3043bcSuch int batt_flags;
816fc3043bcSuch
817fc3043bcSuch APM_LOCK(sc);
818fc3043bcSuch switch (cmd) {
819fc3043bcSuch case APM_IOC_STANDBY:
820fc3043bcSuch if (!apm_do_standby) {
821fc3043bcSuch error = EOPNOTSUPP;
822fc3043bcSuch break;
823fc3043bcSuch }
824fc3043bcSuch
825fc3043bcSuch if ((flag & FWRITE) == 0) {
826fc3043bcSuch error = EBADF;
827fc3043bcSuch break;
828fc3043bcSuch }
829fc3043bcSuch apm_userstandbys++;
830fc3043bcSuch break;
831fc3043bcSuch
832fc3043bcSuch case APM_IOC_SUSPEND:
833fc3043bcSuch if ((flag & FWRITE) == 0) {
834fc3043bcSuch error = EBADF;
835fc3043bcSuch break;
836fc3043bcSuch }
837fc3043bcSuch apm_suspends++;
838fc3043bcSuch break;
839fc3043bcSuch
840fc3043bcSuch case APM_IOC_NEXTEVENT:
8411cfa6cdcSuwe if (!sc->sc_event_count)
842fc3043bcSuch error = EAGAIN;
843fc3043bcSuch else {
844fc3043bcSuch evp = (struct apm_event_info *)data;
8451cfa6cdcSuwe i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
846fc3043bcSuch i %= APM_NEVENTS;
8471cfa6cdcSuwe *evp = sc->sc_event_list[i];
8481cfa6cdcSuwe sc->sc_event_count--;
849fc3043bcSuch }
850fc3043bcSuch break;
851fc3043bcSuch
8521a40a44aScube case OAPM_IOC_GETPOWER:
853fc3043bcSuch case APM_IOC_GETPOWER:
854fc3043bcSuch powerp = (struct apm_power_info *)data;
8559a25b0cbSuwe if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0,
8561cfa6cdcSuwe powerp)) != 0) {
857fc3043bcSuch apm_perror("ioctl get power status", error);
858fc3043bcSuch error = EIO;
859fc3043bcSuch break;
860fc3043bcSuch }
861fc3043bcSuch switch (apm_minver) {
862fc3043bcSuch case 0:
863fc3043bcSuch break;
864fc3043bcSuch case 1:
865fc3043bcSuch default:
8661cfa6cdcSuwe batt_flags = powerp->battery_flags;
867fc3043bcSuch powerp->battery_state = APM_BATT_UNKNOWN;
868fc3043bcSuch if (batt_flags & APM_BATT_FLAG_HIGH)
869fc3043bcSuch powerp->battery_state = APM_BATT_HIGH;
870fc3043bcSuch else if (batt_flags & APM_BATT_FLAG_LOW)
871fc3043bcSuch powerp->battery_state = APM_BATT_LOW;
872fc3043bcSuch else if (batt_flags & APM_BATT_FLAG_CRITICAL)
873fc3043bcSuch powerp->battery_state = APM_BATT_CRITICAL;
874fc3043bcSuch else if (batt_flags & APM_BATT_FLAG_CHARGING)
875fc3043bcSuch powerp->battery_state = APM_BATT_CHARGING;
876fc3043bcSuch else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
877fc3043bcSuch powerp->battery_state = APM_BATT_ABSENT;
878fc3043bcSuch break;
879fc3043bcSuch }
880fc3043bcSuch break;
881fc3043bcSuch
882fc3043bcSuch default:
883fc3043bcSuch error = ENOTTY;
884fc3043bcSuch }
885fc3043bcSuch APM_UNLOCK(sc);
886fc3043bcSuch
887fc3043bcSuch return (error);
888fc3043bcSuch }
889fc3043bcSuch
890fc3043bcSuch int
apmdevpoll(dev_t dev,int events,struct lwp * l)89195e1ffb1Schristos apmdevpoll(dev_t dev, int events, struct lwp *l)
892fc3043bcSuch {
893cf990769Srafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev));
894fc3043bcSuch int revents = 0;
895fc3043bcSuch
896fc3043bcSuch APM_LOCK(sc);
897fc3043bcSuch if (events & (POLLIN | POLLRDNORM)) {
8981cfa6cdcSuwe if (sc->sc_event_count)
899fc3043bcSuch revents |= events & (POLLIN | POLLRDNORM);
900fc3043bcSuch else
90195e1ffb1Schristos selrecord(l, &sc->sc_rsel);
902fc3043bcSuch }
903fc3043bcSuch APM_UNLOCK(sc);
904fc3043bcSuch
905fc3043bcSuch return (revents);
906fc3043bcSuch }
907fc3043bcSuch
908fc3043bcSuch static void
filt_apmrdetach(struct knote * kn)909fc3043bcSuch filt_apmrdetach(struct knote *kn)
910fc3043bcSuch {
911fc3043bcSuch struct apm_softc *sc = kn->kn_hook;
912fc3043bcSuch
913fc3043bcSuch APM_LOCK(sc);
9142682e5acSthorpej selremove_knote(&sc->sc_rsel, kn);
915fc3043bcSuch APM_UNLOCK(sc);
916fc3043bcSuch }
917fc3043bcSuch
918fc3043bcSuch static int
filt_apmread(struct knote * kn,long hint)919168cd830Schristos filt_apmread(struct knote *kn, long hint)
920fc3043bcSuch {
921fc3043bcSuch struct apm_softc *sc = kn->kn_hook;
922fc3043bcSuch
9231cfa6cdcSuwe kn->kn_data = sc->sc_event_count;
924fc3043bcSuch return (kn->kn_data > 0);
925fc3043bcSuch }
926fc3043bcSuch
92718b796d4Smaya static const struct filterops apmread_filtops = {
928*12ae65d9Sthorpej .f_flags = FILTEROP_ISFD,
92918b796d4Smaya .f_attach = NULL,
93018b796d4Smaya .f_detach = filt_apmrdetach,
93118b796d4Smaya .f_event = filt_apmread,
93218b796d4Smaya };
933fc3043bcSuch
934fc3043bcSuch int
apmdevkqfilter(dev_t dev,struct knote * kn)935fc3043bcSuch apmdevkqfilter(dev_t dev, struct knote *kn)
936fc3043bcSuch {
937cf990769Srafal struct apm_softc *sc = device_lookup_private(&apmdev_cd, APMUNIT(dev));
938fc3043bcSuch
939fc3043bcSuch switch (kn->kn_filter) {
940fc3043bcSuch case EVFILT_READ:
941fc3043bcSuch kn->kn_fop = &apmread_filtops;
942fc3043bcSuch break;
943fc3043bcSuch
944fc3043bcSuch default:
9454e38160dSpooka return (EINVAL);
946fc3043bcSuch }
947fc3043bcSuch
948fc3043bcSuch kn->kn_hook = sc;
949fc3043bcSuch
950fc3043bcSuch APM_LOCK(sc);
9512682e5acSthorpej selrecord_knote(&sc->sc_rsel, kn);
952fc3043bcSuch APM_UNLOCK(sc);
953fc3043bcSuch
954fc3043bcSuch return (0);
955fc3043bcSuch }
956