xref: /openbsd/sys/arch/arm64/dev/apm.c (revision a74a190b)
1 /*	$OpenBSD: apm.c,v 1.26 2024/10/30 06:16:27 jsg 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 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/proc.h>
40 #include <sys/device.h>
41 #include <sys/fcntl.h>
42 #include <sys/ioctl.h>
43 #include <sys/event.h>
44 #include <sys/reboot.h>
45 #include <sys/hibernate.h>
46 #include <sys/task.h>
47 
48 #include <machine/conf.h>
49 #include <machine/cpu.h>
50 #include <machine/acpiapm.h>
51 #include <machine/apmvar.h>
52 
53 #if defined(APMDEBUG)
54 #define DPRINTF(x)	printf x
55 #else
56 #define	DPRINTF(x)	/**/
57 #endif
58 
59 #ifdef SUSPEND
60 struct taskq *sleep_taskq;
61 struct task suspend_task;
62 void	do_suspend(void *);
63 #ifdef HIBERNATE
64 struct task hibernate_task;
65 void	do_hibernate(void *);
66 #endif
67 #endif
68 
69 struct apm_softc {
70 	struct device sc_dev;
71 	struct klist sc_note;
72 	int    sc_flags;
73 };
74 
75 int apmmatch(struct device *, void *, void *);
76 void apmattach(struct device *, struct device *, void *);
77 
78 const struct cfattach apm_ca = {
79 	sizeof(struct apm_softc), apmmatch, apmattach
80 };
81 
82 struct cfdriver apm_cd = {
83 	NULL, "apm", DV_DULL
84 };
85 
86 #define	APMUNIT(dev)	(minor(dev)&0xf0)
87 #define	APMDEV(dev)	(minor(dev)&0x0f)
88 #define APMDEV_NORMAL	0
89 #define APMDEV_CTL	8
90 
91 void filt_apmrdetach(struct knote *kn);
92 int filt_apmread(struct knote *kn, long hint);
93 int apmkqfilter(dev_t dev, struct knote *kn);
94 int apm_getdefaultinfo(struct apm_power_info *);
95 
96 const struct filterops apmread_filtops = {
97 	.f_flags	= FILTEROP_ISFD,
98 	.f_attach	= NULL,
99 	.f_detach	= filt_apmrdetach,
100 	.f_event	= filt_apmread,
101 };
102 
103 int (*get_apminfo)(struct apm_power_info *) = apm_getdefaultinfo;
104 
105 /*
106  * Flags to control kernel display
107  *	SCFLAG_NOPRINT:		do not output APM power messages due to
108  *				a power change event.
109  *
110  *	SCFLAG_PCTPRINT:	do not output APM power messages due to
111  *				to a power change event unless the battery
112  *				percentage changes.
113  */
114 
115 #define SCFLAG_NOPRINT	0x0008000
116 #define SCFLAG_PCTPRINT	0x0004000
117 #define SCFLAG_PRINT	(SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
118 
119 #define	SCFLAG_OREAD	(1 << 0)
120 #define	SCFLAG_OWRITE	(1 << 1)
121 #define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)
122 
123 
124 int
apmmatch(struct device * parent,void * match,void * aux)125 apmmatch(struct device *parent, void *match, void *aux)
126 {
127 	return (1);
128 }
129 
130 void
apmattach(struct device * parent,struct device * self,void * aux)131 apmattach(struct device *parent, struct device *self, void *aux)
132 {
133 #ifdef SUSPEND
134 	sleep_taskq = taskq_create("sleep", 1, IPL_NONE, 0);
135 	task_set(&suspend_task, do_suspend, NULL);
136 #ifdef HIBERNATE
137 	task_set(&hibernate_task, do_hibernate, NULL);
138 #endif
139 #endif
140 
141 	acpiapm_open = apmopen;
142 	acpiapm_close = apmclose;
143 	acpiapm_ioctl = apmioctl;
144 	acpiapm_kqfilter = apmkqfilter;
145 
146 	printf("\n");
147 }
148 
149 int
apmopen(dev_t dev,int flag,int mode,struct proc * p)150 apmopen(dev_t dev, int flag, int mode, struct proc *p)
151 {
152 	struct apm_softc *sc;
153 	int error = 0;
154 
155 	/* apm0 only */
156 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
157 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
158 		return ENXIO;
159 
160 	DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
161 	    APMDEV(dev), p->p_p->ps_pid, flag, mode));
162 
163 	switch (APMDEV(dev)) {
164 	case APMDEV_CTL:
165 		if (!(flag & FWRITE)) {
166 			error = EINVAL;
167 			break;
168 		}
169 		if (sc->sc_flags & SCFLAG_OWRITE) {
170 			error = EBUSY;
171 			break;
172 		}
173 		sc->sc_flags |= SCFLAG_OWRITE;
174 		break;
175 	case APMDEV_NORMAL:
176 		if (!(flag & FREAD) || (flag & FWRITE)) {
177 			error = EINVAL;
178 			break;
179 		}
180 		sc->sc_flags |= SCFLAG_OREAD;
181 		break;
182 	default:
183 		error = ENXIO;
184 		break;
185 	}
186 	return error;
187 }
188 
189 int
apmclose(dev_t dev,int flag,int mode,struct proc * p)190 apmclose(dev_t dev, int flag, int mode, struct proc *p)
191 {
192 	struct apm_softc *sc;
193 
194 	/* apm0 only */
195 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
196 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
197 		return ENXIO;
198 
199 	DPRINTF(("apmclose: pid %d flag %x mode %x\n",
200 	    p->p_p->ps_pid, flag, mode));
201 
202 	switch (APMDEV(dev)) {
203 	case APMDEV_CTL:
204 		sc->sc_flags &= ~SCFLAG_OWRITE;
205 		break;
206 	case APMDEV_NORMAL:
207 		sc->sc_flags &= ~SCFLAG_OREAD;
208 		break;
209 	}
210 	return 0;
211 }
212 
213 int
apmioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)214 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
215 {
216 	struct apm_softc *sc;
217 	struct apm_power_info *power;
218 	int error = 0;
219 
220 	/* apm0 only */
221 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
222 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
223 		return ENXIO;
224 
225 	switch (cmd) {
226 #ifdef SUSPEND
227 	case APM_IOC_STANDBY:
228 	case APM_IOC_SUSPEND:
229 		if ((flag & FWRITE) == 0) {
230 			error = EBADF;
231 			break;
232 		}
233 		error = request_sleep(SLEEP_SUSPEND);
234 		break;
235 #ifdef HIBERNATE
236 	case APM_IOC_HIBERNATE:
237 		if ((error = suser(p)) != 0)
238 			break;
239 		if ((flag & FWRITE) == 0) {
240 			error = EBADF;
241 			break;
242 		}
243 		error = request_sleep(SLEEP_HIBERNATE);
244 		break;
245 #endif
246 #endif
247 	case APM_IOC_PRN_CTL:
248 		if ((flag & FWRITE) == 0)
249 			error = EBADF;
250 		else {
251 			int flag = *(int *)data;
252 			DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
253 			switch (flag) {
254 			case APM_PRINT_ON:	/* enable printing */
255 				sc->sc_flags &= ~SCFLAG_PRINT;
256 				break;
257 			case APM_PRINT_OFF: /* disable printing */
258 				sc->sc_flags &= ~SCFLAG_PRINT;
259 				sc->sc_flags |= SCFLAG_NOPRINT;
260 				break;
261 			case APM_PRINT_PCT: /* disable some printing */
262 				sc->sc_flags &= ~SCFLAG_PRINT;
263 				sc->sc_flags |= SCFLAG_PCTPRINT;
264 				break;
265 			default:
266 				error = EINVAL;
267 				break;
268 			}
269 		}
270 		break;
271 	case APM_IOC_GETPOWER:
272 		power = (struct apm_power_info *)data;
273 		error = (*get_apminfo)(power);
274 		break;
275 	default:
276 		error = ENOTTY;
277 	}
278 
279 	return error;
280 }
281 
282 void
filt_apmrdetach(struct knote * kn)283 filt_apmrdetach(struct knote *kn)
284 {
285 	struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
286 
287 	klist_remove_locked(&sc->sc_note, kn);
288 }
289 
290 int
filt_apmread(struct knote * kn,long hint)291 filt_apmread(struct knote *kn, long hint)
292 {
293 	/* XXX weird kqueue_scan() semantics */
294 	if (hint && !kn->kn_data)
295 		kn->kn_data = (int)hint;
296 
297 	return (1);
298 }
299 
300 int
apmkqfilter(dev_t dev,struct knote * kn)301 apmkqfilter(dev_t dev, struct knote *kn)
302 {
303 	struct apm_softc *sc;
304 
305 	/* apm0 only */
306 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
307 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
308 		return ENXIO;
309 
310 	switch (kn->kn_filter) {
311 	case EVFILT_READ:
312 		kn->kn_fop = &apmread_filtops;
313 		break;
314 	default:
315 		return (EINVAL);
316 	}
317 
318 	kn->kn_hook = (caddr_t)sc;
319 	klist_insert_locked(&sc->sc_note, kn);
320 
321 	return (0);
322 }
323 
324 int
apm_getdefaultinfo(struct apm_power_info * info)325 apm_getdefaultinfo(struct apm_power_info *info)
326 {
327 	info->battery_state = APM_BATT_UNKNOWN;
328 	info->ac_state = APM_AC_UNKNOWN;
329 	info->battery_life = 0;
330 	info->minutes_left = -1;
331 	return (0);
332 }
333 
334 void
apm_setinfohook(int (* hook)(struct apm_power_info *))335 apm_setinfohook(int (*hook)(struct apm_power_info *))
336 {
337 	get_apminfo = hook;
338 }
339 
340 int
apm_record_event(u_int event)341 apm_record_event(u_int event)
342 {
343 	struct apm_softc *sc = apm_cd.cd_devs[0];
344 	static int apm_evindex;
345 
346 	/* skip if no user waiting */
347 	if (sc == NULL || (sc->sc_flags & SCFLAG_OPEN) == 0)
348 		return 1;
349 
350 	apm_evindex++;
351 	knote_locked(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex));
352 	return 0;
353 }
354 
355 #ifdef SUSPEND
356 
357 void
do_suspend(void * v)358 do_suspend(void *v)
359 {
360 	sleep_state(v, SLEEP_SUSPEND);
361 }
362 
363 #ifdef HIBERNATE
364 void
do_hibernate(void * v)365 do_hibernate(void *v)
366 {
367 	sleep_state(v, SLEEP_HIBERNATE);
368 }
369 #endif
370 
371 int
request_sleep(int sleepmode)372 request_sleep(int sleepmode)
373 {
374 	if (sleep_taskq == NULL)
375 		return EINVAL;
376 
377 	switch (sleepmode) {
378 	case SLEEP_SUSPEND:
379 		task_add(sleep_taskq, &suspend_task);
380 		break;
381 #ifdef HIBERNATE
382 	case SLEEP_HIBERNATE:
383 		if (get_hibernate_io_function(swdevt[0]) == NULL)
384 			return EOPNOTSUPP;
385 		task_add(sleep_taskq, &hibernate_task);
386 		break;
387 #endif
388 	}
389 
390 	return 0;
391 }
392 
393 #ifdef MULTIPROCESSOR
394 
395 void
sleep_mp(void)396 sleep_mp(void)
397 {
398 	CPU_INFO_ITERATOR cii;
399 	struct cpu_info *ci;
400 
401 	CPU_INFO_FOREACH(cii, ci) {
402 		if (CPU_IS_PRIMARY(ci))
403 			continue;
404 		arm_send_ipi(ci, ARM_IPI_HALT);
405 		while (ci->ci_flags & CPUF_RUNNING)
406 			CPU_BUSY_CYCLE();
407 	}
408 }
409 
410 void
resume_mp(void)411 resume_mp(void)
412 {
413 	CPU_INFO_ITERATOR cii;
414 	struct cpu_info *ci;
415 
416 	CPU_INFO_FOREACH(cii, ci) {
417 		if (CPU_IS_PRIMARY(ci))
418 			continue;
419 		cpu_resume_secondary(ci);
420 	}
421 	cpu_boot_secondary_processors();
422 }
423 
424 #endif /* MULTIPROCESSOR */
425 
426 int
sleep_showstate(void * v,int sleepmode)427 sleep_showstate(void *v, int sleepmode)
428 {
429 	if (sleepmode == SLEEP_SUSPEND)
430 		return 0;
431 
432 	return EOPNOTSUPP;
433 }
434 
435 int
sleep_setstate(void * v)436 sleep_setstate(void *v)
437 {
438 	return 0;
439 }
440 
441 int
gosleep(void * v)442 gosleep(void *v)
443 {
444 	return cpu_suspend_primary();
445 }
446 
447 int
sleep_resume(void * v)448 sleep_resume(void *v)
449 {
450 	return 0;
451 }
452 
453 int
suspend_finish(void * v)454 suspend_finish(void *v)
455 {
456 	apm_record_event(APM_NORMAL_RESUME);
457 	return 0;
458 }
459 
460 #endif /* SUSPEND */
461