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