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