xref: /openbsd/sys/arch/arm64/dev/apm.c (revision 4cfece93)
1 /*	$OpenBSD: apm.c,v 1.5 2020/05/29 04:42:23 deraadt Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 Alexander Guy.  All rights reserved.
5  * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
6  * Copyright (c) 1995 John T. Kohl.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the names of the authors nor the names of contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 
34 #include "apm.h"
35 #include "wsdisplay.h"
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/proc.h>
41 #include <sys/device.h>
42 #include <sys/fcntl.h>
43 #include <sys/ioctl.h>
44 #include <sys/buf.h>
45 #include <sys/event.h>
46 #include <sys/reboot.h>
47 #include <sys/hibernate.h>
48 
49 #include <machine/conf.h>
50 #include <machine/cpu.h>
51 #include <machine/acpiapm.h>
52 #include <machine/apmvar.h>
53 
54 #include <dev/wscons/wsdisplayvar.h>
55 
56 #if defined(APMDEBUG)
57 #define DPRINTF(x)	printf x
58 #else
59 #define	DPRINTF(x)	/**/
60 #endif
61 
62 struct apm_softc {
63 	struct device sc_dev;
64 	struct klist sc_note;
65 	int    sc_flags;
66 };
67 
68 int apmmatch(struct device *, void *, void *);
69 void apmattach(struct device *, struct device *, void *);
70 
71 struct cfattach apm_ca = {
72 	sizeof(struct apm_softc), apmmatch, apmattach
73 };
74 
75 struct cfdriver apm_cd = {
76 	NULL, "apm", DV_DULL
77 };
78 
79 #define	APMUNIT(dev)	(minor(dev)&0xf0)
80 #define	APMDEV(dev)	(minor(dev)&0x0f)
81 #define APMDEV_NORMAL	0
82 #define APMDEV_CTL	8
83 
84 void filt_apmrdetach(struct knote *kn);
85 int filt_apmread(struct knote *kn, long hint);
86 int apmkqfilter(dev_t dev, struct knote *kn);
87 int apm_getdefaultinfo(struct apm_power_info *);
88 
89 const struct filterops apmread_filtops = {
90 	.f_flags	= FILTEROP_ISFD,
91 	.f_attach	= NULL,
92 	.f_detach	= filt_apmrdetach,
93 	.f_event	= filt_apmread,
94 };
95 
96 int (*get_apminfo)(struct apm_power_info *) = apm_getdefaultinfo;
97 
98 /*
99  * Flags to control kernel display
100  *	SCFLAG_NOPRINT:		do not output APM power messages due to
101  *				a power change event.
102  *
103  *	SCFLAG_PCTPRINT:	do not output APM power messages due to
104  *				to a power change event unless the battery
105  *				percentage changes.
106  */
107 
108 #define SCFLAG_NOPRINT	0x0008000
109 #define SCFLAG_PCTPRINT	0x0004000
110 #define SCFLAG_PRINT	(SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
111 
112 #define	SCFLAG_OREAD 	(1 << 0)
113 #define	SCFLAG_OWRITE	(1 << 1)
114 #define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)
115 
116 
117 int
118 apmmatch(struct device *parent, void *match, void *aux)
119 {
120 	return (1);
121 }
122 
123 void
124 apmattach(struct device *parent, struct device *self, void *aux)
125 {
126 	acpiapm_open = apmopen;
127 	acpiapm_close = apmclose;
128 	acpiapm_ioctl = apmioctl;
129 	acpiapm_kqfilter = apmkqfilter;
130 
131 	printf("\n");
132 }
133 
134 int
135 apmopen(dev_t dev, int flag, int mode, struct proc *p)
136 {
137 	struct apm_softc *sc;
138 	int error = 0;
139 
140 	/* apm0 only */
141 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
142 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
143 		return ENXIO;
144 
145 	DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
146 	    APMDEV(dev), p->p_p->ps_pid, flag, mode));
147 
148 	switch (APMDEV(dev)) {
149 	case APMDEV_CTL:
150 		if (!(flag & FWRITE)) {
151 			error = EINVAL;
152 			break;
153 		}
154 		if (sc->sc_flags & SCFLAG_OWRITE) {
155 			error = EBUSY;
156 			break;
157 		}
158 		sc->sc_flags |= SCFLAG_OWRITE;
159 		break;
160 	case APMDEV_NORMAL:
161 		if (!(flag & FREAD) || (flag & FWRITE)) {
162 			error = EINVAL;
163 			break;
164 		}
165 		sc->sc_flags |= SCFLAG_OREAD;
166 		break;
167 	default:
168 		error = ENXIO;
169 		break;
170 	}
171 	return error;
172 }
173 
174 int
175 apmclose(dev_t dev, int flag, int mode, struct proc *p)
176 {
177 	struct apm_softc *sc;
178 
179 	/* apm0 only */
180 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
181 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
182 		return ENXIO;
183 
184 	DPRINTF(("apmclose: pid %d flag %x mode %x\n",
185 	    p->p_p->ps_pid, flag, mode));
186 
187 	switch (APMDEV(dev)) {
188 	case APMDEV_CTL:
189 		sc->sc_flags &= ~SCFLAG_OWRITE;
190 		break;
191 	case APMDEV_NORMAL:
192 		sc->sc_flags &= ~SCFLAG_OREAD;
193 		break;
194 	}
195 	return 0;
196 }
197 
198 int
199 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
200 {
201 	struct apm_softc *sc;
202 	struct apm_power_info *power;
203 	int error = 0;
204 
205 	/* apm0 only */
206 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
207 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
208 		return ENXIO;
209 
210 	switch (cmd) {
211 		/* some ioctl names from linux */
212 	case APM_IOC_STANDBY:
213 	case APM_IOC_STANDBY_REQ:
214 	case APM_IOC_SUSPEND:
215 	case APM_IOC_SUSPEND_REQ:
216 		if ((flag & FWRITE) == 0)
217 			error = EBADF;
218 		error = EOPNOTSUPP;
219 		break;
220 #ifdef HIBERNATE
221 	case APM_IOC_HIBERNATE:
222 		if ((flag & FWRITE) == 0)
223 			error = EBADF;
224 		error = EOPNOTSUPP;
225 		break;
226 #endif
227 	case APM_IOC_PRN_CTL:
228 		if ((flag & FWRITE) == 0)
229 			error = EBADF;
230 		else {
231 			int flag = *(int *)data;
232 			DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
233 			switch (flag) {
234 			case APM_PRINT_ON:	/* enable printing */
235 				sc->sc_flags &= ~SCFLAG_PRINT;
236 				break;
237 			case APM_PRINT_OFF: /* disable printing */
238 				sc->sc_flags &= ~SCFLAG_PRINT;
239 				sc->sc_flags |= SCFLAG_NOPRINT;
240 				break;
241 			case APM_PRINT_PCT: /* disable some printing */
242 				sc->sc_flags &= ~SCFLAG_PRINT;
243 				sc->sc_flags |= SCFLAG_PCTPRINT;
244 				break;
245 			default:
246 				error = EINVAL;
247 				break;
248 			}
249 		}
250 		break;
251 	case APM_IOC_DEV_CTL:
252 		if ((flag & FWRITE) == 0)
253 			error = EBADF;
254 		else
255 			error = EOPNOTSUPP; /* XXX */
256 		break;
257 	case APM_IOC_GETPOWER:
258 	        power = (struct apm_power_info *)data;
259 		error = (*get_apminfo)(power);
260 		break;
261 	default:
262 		error = ENOTTY;
263 	}
264 
265 	return error;
266 }
267 
268 void
269 filt_apmrdetach(struct knote *kn)
270 {
271 	struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
272 
273 	klist_remove(&sc->sc_note, kn);
274 }
275 
276 int
277 filt_apmread(struct knote *kn, long hint)
278 {
279 	/* XXX weird kqueue_scan() semantics */
280 	if (hint && !kn->kn_data)
281 		kn->kn_data = (int)hint;
282 
283 	return (1);
284 }
285 
286 int
287 apmkqfilter(dev_t dev, struct knote *kn)
288 {
289 	struct apm_softc *sc;
290 
291 	/* apm0 only */
292 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
293 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
294 		return ENXIO;
295 
296 	switch (kn->kn_filter) {
297 	case EVFILT_READ:
298 		kn->kn_fop = &apmread_filtops;
299 		break;
300 	default:
301 		return (EINVAL);
302 	}
303 
304 	kn->kn_hook = (caddr_t)sc;
305 	klist_insert(&sc->sc_note, kn);
306 
307 	return (0);
308 }
309 
310 int
311 apm_getdefaultinfo(struct apm_power_info *info)
312 {
313 	info->battery_state = APM_BATT_UNKNOWN;
314 	info->ac_state = APM_AC_UNKNOWN;
315 	info->battery_life = 0;
316 	info->minutes_left = -1;
317 	return (0);
318 }
319 
320 void
321 apm_setinfohook(int (*hook)(struct apm_power_info *))
322 {
323 	get_apminfo = hook;
324 }
325 
326 int
327 apm_record_event(u_int event, const char *src, const char *msg)
328 {
329 	static int apm_evindex;
330 	struct apm_softc *sc;
331 
332 	/* apm0 only */
333 	if (apm_cd.cd_ndevs == 0 || (sc = apm_cd.cd_devs[0]) == NULL)
334 		return ENXIO;
335 
336 	if ((sc->sc_flags & SCFLAG_NOPRINT) == 0)
337 		printf("%s: %s %s\n", sc->sc_dev.dv_xname, src, msg);
338 
339 	/* skip if no user waiting */
340 	if ((sc->sc_flags & SCFLAG_OPEN) == 0)
341 		return (1);
342 
343 	apm_evindex++;
344 	KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex));
345 
346 	return (0);
347 }
348