xref: /netbsd-src/sys/dev/hpc/apm/apmdev.c (revision 12ae65d98c188ed1269ec99f9ef70653dfdd5bf1)
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