1*8e8b3a2cSjsg /* $OpenBSD: apm.c,v 1.37 2024/05/29 06:39:13 jsg Exp $ */
2b0857fefSdrahn
3d9076e62Sdrahn /*-
4d9076e62Sdrahn * Copyright (c) 2001 Alexander Guy. All rights reserved.
5d9076e62Sdrahn * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
6d9076e62Sdrahn * Copyright (c) 1995 John T. Kohl. All rights reserved.
7d9076e62Sdrahn *
8d9076e62Sdrahn * Redistribution and use in source and binary forms, with or without
9d9076e62Sdrahn * modification, are permitted provided that the following conditions
10d9076e62Sdrahn * are met:
11d9076e62Sdrahn * 1. Redistributions of source code must retain the above copyright
12d9076e62Sdrahn * notice, this list of conditions and the following disclaimer.
13d9076e62Sdrahn * 2. Redistributions in binary form must reproduce the above copyright
14d9076e62Sdrahn * notice, this list of conditions and the following disclaimer in the
15d9076e62Sdrahn * documentation and/or other materials provided with the distribution.
16b386efcbStedu * 3. Neither the names of the authors nor the names of contributors
17d9076e62Sdrahn * may be used to endorse or promote products derived from this software
18d9076e62Sdrahn * without specific prior written permission.
19d9076e62Sdrahn *
20b386efcbStedu * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21d9076e62Sdrahn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22d9076e62Sdrahn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23b386efcbStedu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24d9076e62Sdrahn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25d9076e62Sdrahn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26d9076e62Sdrahn * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27d9076e62Sdrahn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28d9076e62Sdrahn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29d9076e62Sdrahn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30d9076e62Sdrahn * SUCH DAMAGE.
31d9076e62Sdrahn *
32d9076e62Sdrahn */
33d9076e62Sdrahn
34d9076e62Sdrahn #include "apm.h"
35d9076e62Sdrahn
36d9076e62Sdrahn #if NAPM > 1
37d9076e62Sdrahn #error only one APM emulation device may be configured
38d9076e62Sdrahn #endif
39d9076e62Sdrahn
40d9076e62Sdrahn #include <sys/param.h>
41d9076e62Sdrahn #include <sys/systm.h>
42d9076e62Sdrahn #include <sys/kernel.h>
43d9076e62Sdrahn #include <sys/proc.h>
44d9076e62Sdrahn #include <sys/device.h>
45d9076e62Sdrahn #include <sys/fcntl.h>
46d9076e62Sdrahn #include <sys/ioctl.h>
47d9076e62Sdrahn #include <sys/event.h>
48d9076e62Sdrahn
49d9076e62Sdrahn #include <machine/conf.h>
50d9076e62Sdrahn #include <machine/cpu.h>
51d9076e62Sdrahn #include <machine/apmvar.h>
526ca343deSmpi #include <machine/autoconf.h>
53d9076e62Sdrahn
54d9076e62Sdrahn #include <macppc/dev/pm_direct.h>
55d9076e62Sdrahn
56d9076e62Sdrahn #if defined(APMDEBUG)
57d9076e62Sdrahn #define DPRINTF(x) printf x
58d9076e62Sdrahn #else
59d9076e62Sdrahn #define DPRINTF(x) /**/
60d9076e62Sdrahn #endif
61d9076e62Sdrahn
62d9076e62Sdrahn struct apm_softc {
63d9076e62Sdrahn struct device sc_dev;
64d9076e62Sdrahn struct klist sc_note;
65d9076e62Sdrahn int sc_flags;
66d9076e62Sdrahn };
67d9076e62Sdrahn
68c4071fd1Smillert int apmmatch(struct device *, void *, void *);
69c4071fd1Smillert void apmattach(struct device *, struct device *, void *);
70d9076e62Sdrahn
7189ed722cSmpi const struct cfattach apm_ca = {
72d9076e62Sdrahn sizeof(struct apm_softc), apmmatch, apmattach
73d9076e62Sdrahn };
74d9076e62Sdrahn
75d9076e62Sdrahn struct cfdriver apm_cd = {
76d9076e62Sdrahn NULL, "apm", DV_DULL
77d9076e62Sdrahn };
78d9076e62Sdrahn
79d9076e62Sdrahn #define APMUNIT(dev) (minor(dev)&0xf0)
80d9076e62Sdrahn #define APMDEV(dev) (minor(dev)&0x0f)
81d9076e62Sdrahn #define APMDEV_NORMAL 0
82d9076e62Sdrahn #define APMDEV_CTL 8
83d9076e62Sdrahn
84c4071fd1Smillert void filt_apmrdetach(struct knote *kn);
85c4071fd1Smillert int filt_apmread(struct knote *kn, long hint);
86c4071fd1Smillert int apmkqfilter(dev_t dev, struct knote *kn);
87d9076e62Sdrahn
8894321eb4Svisa const struct filterops apmread_filtops = {
89b8213689Svisa .f_flags = FILTEROP_ISFD,
9094321eb4Svisa .f_attach = NULL,
9194321eb4Svisa .f_detach = filt_apmrdetach,
9294321eb4Svisa .f_event = filt_apmread,
9394321eb4Svisa };
94d9076e62Sdrahn
95d9076e62Sdrahn /*
96d9076e62Sdrahn * Flags to control kernel display
97d9076e62Sdrahn * SCFLAG_NOPRINT: do not output APM power messages due to
98d9076e62Sdrahn * a power change event.
99d9076e62Sdrahn *
100d9076e62Sdrahn * SCFLAG_PCTPRINT: do not output APM power messages due to
101d9076e62Sdrahn * to a power change event unless the battery
102d9076e62Sdrahn * percentage changes.
103d9076e62Sdrahn */
104d9076e62Sdrahn
105d9076e62Sdrahn #define SCFLAG_NOPRINT 0x0008000
106d9076e62Sdrahn #define SCFLAG_PCTPRINT 0x0004000
107d9076e62Sdrahn #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
108d9076e62Sdrahn
109d9076e62Sdrahn #define SCFLAG_OREAD (1 << 0)
110d9076e62Sdrahn #define SCFLAG_OWRITE (1 << 1)
111d9076e62Sdrahn #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
112d9076e62Sdrahn
113d9076e62Sdrahn
114d9076e62Sdrahn int
apmmatch(struct device * parent,void * match,void * aux)115093da1aaSdrahn apmmatch(struct device *parent, void *match, void *aux)
116d9076e62Sdrahn {
1176ca343deSmpi struct confargs *ca = aux;
1184425d974Smiod
1196ca343deSmpi if (strcmp(ca->ca_name, "apm") != 0)
1206ca343deSmpi return (0);
1214425d974Smiod
1226ca343deSmpi return (1);
123d9076e62Sdrahn }
124d9076e62Sdrahn
125d9076e62Sdrahn void
apmattach(struct device * parent,struct device * self,void * aux)126093da1aaSdrahn apmattach(struct device *parent, struct device *self, void *aux)
127d9076e62Sdrahn {
128d9076e62Sdrahn struct pmu_battery_info info;
129d9076e62Sdrahn
130d9076e62Sdrahn pm_battery_info(0, &info);
131d9076e62Sdrahn
132d9076e62Sdrahn printf(": battery flags 0x%X, ", info.flags);
133d9076e62Sdrahn printf("%d%% charged\n", ((info.cur_charge * 100) / info.max_charge));
134bdedaf02Sgkoehler
135bdedaf02Sgkoehler #ifdef SUSPEND
136bdedaf02Sgkoehler device_register_wakeup(self);
137bdedaf02Sgkoehler #endif
138d9076e62Sdrahn }
139d9076e62Sdrahn
140d9076e62Sdrahn int
apmopen(dev_t dev,int flag,int mode,struct proc * p)141093da1aaSdrahn apmopen(dev_t dev, int flag, int mode, struct proc *p)
142d9076e62Sdrahn {
143d9076e62Sdrahn struct apm_softc *sc;
144d9076e62Sdrahn int error = 0;
145d9076e62Sdrahn
146d9076e62Sdrahn /* apm0 only */
147d9076e62Sdrahn if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
148d9076e62Sdrahn !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
149d9076e62Sdrahn return ENXIO;
150d9076e62Sdrahn
151d9076e62Sdrahn DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
152552f6074Sguenther APMDEV(dev), p->p_p->ps_pid, flag, mode));
153d9076e62Sdrahn
154d9076e62Sdrahn switch (APMDEV(dev)) {
155d9076e62Sdrahn case APMDEV_CTL:
156d9076e62Sdrahn if (!(flag & FWRITE)) {
157d9076e62Sdrahn error = EINVAL;
158d9076e62Sdrahn break;
159d9076e62Sdrahn }
160d9076e62Sdrahn if (sc->sc_flags & SCFLAG_OWRITE) {
161d9076e62Sdrahn error = EBUSY;
162d9076e62Sdrahn break;
163d9076e62Sdrahn }
164d9076e62Sdrahn sc->sc_flags |= SCFLAG_OWRITE;
165d9076e62Sdrahn break;
166d9076e62Sdrahn case APMDEV_NORMAL:
167d9076e62Sdrahn if (!(flag & FREAD) || (flag & FWRITE)) {
168d9076e62Sdrahn error = EINVAL;
169d9076e62Sdrahn break;
170d9076e62Sdrahn }
171d9076e62Sdrahn sc->sc_flags |= SCFLAG_OREAD;
172d9076e62Sdrahn break;
173d9076e62Sdrahn default:
174d9076e62Sdrahn error = ENXIO;
175d9076e62Sdrahn break;
176d9076e62Sdrahn }
177d9076e62Sdrahn return error;
178d9076e62Sdrahn }
179d9076e62Sdrahn
180d9076e62Sdrahn int
apmclose(dev_t dev,int flag,int mode,struct proc * p)181093da1aaSdrahn apmclose(dev_t dev, int flag, int mode, struct proc *p)
182d9076e62Sdrahn {
183d9076e62Sdrahn struct apm_softc *sc;
184d9076e62Sdrahn
185d9076e62Sdrahn /* apm0 only */
186d9076e62Sdrahn if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
187d9076e62Sdrahn !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
188d9076e62Sdrahn return ENXIO;
189d9076e62Sdrahn
190552f6074Sguenther DPRINTF(("apmclose: pid %d flag %x mode %x\n",
191552f6074Sguenther p->p_p->ps_pid, flag, mode));
192d9076e62Sdrahn
193d9076e62Sdrahn switch (APMDEV(dev)) {
194d9076e62Sdrahn case APMDEV_CTL:
195d9076e62Sdrahn sc->sc_flags &= ~SCFLAG_OWRITE;
196d9076e62Sdrahn break;
197d9076e62Sdrahn case APMDEV_NORMAL:
198d9076e62Sdrahn sc->sc_flags &= ~SCFLAG_OREAD;
199d9076e62Sdrahn break;
200d9076e62Sdrahn }
201d9076e62Sdrahn return 0;
202d9076e62Sdrahn }
203d9076e62Sdrahn
204d9076e62Sdrahn int
apmioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)205093da1aaSdrahn apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
206d9076e62Sdrahn {
207d9076e62Sdrahn struct apm_softc *sc;
208d9076e62Sdrahn struct pmu_battery_info batt;
209d9076e62Sdrahn struct apm_power_info *power;
210d9076e62Sdrahn int error = 0;
211d9076e62Sdrahn
212d9076e62Sdrahn /* apm0 only */
213d9076e62Sdrahn if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
214d9076e62Sdrahn !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
215d9076e62Sdrahn return ENXIO;
216d9076e62Sdrahn
217d9076e62Sdrahn switch (cmd) {
2187d04ab44Sderaadt #ifdef SUSPEND
219d9076e62Sdrahn case APM_IOC_STANDBY:
220d9076e62Sdrahn case APM_IOC_SUSPEND:
221e1938dd5Sgkoehler if ((flag & FWRITE) == 0) {
222d9076e62Sdrahn error = EBADF;
223e1938dd5Sgkoehler break;
224e1938dd5Sgkoehler }
225e1938dd5Sgkoehler sleep_state(sc, SLEEP_SUSPEND);
226d9076e62Sdrahn break;
2277d04ab44Sderaadt #endif
228d9076e62Sdrahn case APM_IOC_PRN_CTL:
229d9076e62Sdrahn if ((flag & FWRITE) == 0)
230d9076e62Sdrahn error = EBADF;
231d9076e62Sdrahn else {
232d9076e62Sdrahn int flag = *(int *)data;
233d9076e62Sdrahn DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
234d9076e62Sdrahn switch (flag) {
235d9076e62Sdrahn case APM_PRINT_ON: /* enable printing */
236d9076e62Sdrahn sc->sc_flags &= ~SCFLAG_PRINT;
237d9076e62Sdrahn break;
238d9076e62Sdrahn case APM_PRINT_OFF: /* disable printing */
239d9076e62Sdrahn sc->sc_flags &= ~SCFLAG_PRINT;
240d9076e62Sdrahn sc->sc_flags |= SCFLAG_NOPRINT;
241d9076e62Sdrahn break;
242d9076e62Sdrahn case APM_PRINT_PCT: /* disable some printing */
243d9076e62Sdrahn sc->sc_flags &= ~SCFLAG_PRINT;
244d9076e62Sdrahn sc->sc_flags |= SCFLAG_PCTPRINT;
245d9076e62Sdrahn break;
246d9076e62Sdrahn default:
247d9076e62Sdrahn error = EINVAL;
248d9076e62Sdrahn break;
249d9076e62Sdrahn }
250d9076e62Sdrahn }
251d9076e62Sdrahn break;
252d9076e62Sdrahn case APM_IOC_GETPOWER:
253d9076e62Sdrahn power = (struct apm_power_info *)data;
254d9076e62Sdrahn
255d9076e62Sdrahn pm_battery_info(0, &batt);
256d9076e62Sdrahn
257277b493dSmiod power->ac_state = ((batt.flags & PMU_PWR_AC_PRESENT) ?
258277b493dSmiod APM_AC_ON : APM_AC_OFF);
259277b493dSmiod power->battery_life =
260277b493dSmiod ((batt.cur_charge * 100) / batt.max_charge);
261d9076e62Sdrahn
262277b493dSmiod /*
263277b493dSmiod * If the battery is charging, return the minutes left until
264277b493dSmiod * charging is complete. apmd knows this.
265d9076e62Sdrahn */
266d9076e62Sdrahn
267d9076e62Sdrahn if (!(batt.flags & PMU_PWR_BATT_PRESENT)) {
268d9076e62Sdrahn power->battery_state = APM_BATT_UNKNOWN;
269d9076e62Sdrahn power->minutes_left = 0;
270d9076e62Sdrahn power->battery_life = 0;
271d9076e62Sdrahn } else if ((power->ac_state == APM_AC_ON) &&
272d9076e62Sdrahn (batt.draw > 0)) {
273d9076e62Sdrahn power->minutes_left =
274277b493dSmiod (((batt.max_charge - batt.cur_charge) * 3600) /
275277b493dSmiod batt.draw) / 60;
276d9076e62Sdrahn power->battery_state = APM_BATT_CHARGING;
277d9076e62Sdrahn } else {
278d9076e62Sdrahn power->minutes_left =
279d9076e62Sdrahn ((batt.cur_charge * 3600) / (-batt.draw)) / 60;
280d9076e62Sdrahn
281438e94d1Sfcambus if (power->battery_life > 50)
282d9076e62Sdrahn power->battery_state = APM_BATT_HIGH;
283438e94d1Sfcambus else if (power->battery_life > 25)
284d9076e62Sdrahn power->battery_state = APM_BATT_LOW;
285438e94d1Sfcambus else
286438e94d1Sfcambus power->battery_state = APM_BATT_CRITICAL;
287d9076e62Sdrahn }
288d9076e62Sdrahn break;
289d9076e62Sdrahn default:
290d9076e62Sdrahn error = ENOTTY;
291d9076e62Sdrahn }
292d9076e62Sdrahn
293d9076e62Sdrahn return error;
294d9076e62Sdrahn }
295d9076e62Sdrahn
296d9076e62Sdrahn void
filt_apmrdetach(struct knote * kn)297093da1aaSdrahn filt_apmrdetach(struct knote *kn)
298d9076e62Sdrahn {
299d9076e62Sdrahn struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
300d9076e62Sdrahn
3019b0cf67bSvisa klist_remove_locked(&sc->sc_note, kn);
302d9076e62Sdrahn }
303d9076e62Sdrahn
304d9076e62Sdrahn int
filt_apmread(struct knote * kn,long hint)305093da1aaSdrahn filt_apmread(struct knote *kn, long hint)
306d9076e62Sdrahn {
307d9076e62Sdrahn /* XXX weird kqueue_scan() semantics */
308d9076e62Sdrahn if (hint && !kn->kn_data)
309d9076e62Sdrahn kn->kn_data = (int)hint;
310d9076e62Sdrahn
311d9076e62Sdrahn return (1);
312d9076e62Sdrahn }
313d9076e62Sdrahn
314d9076e62Sdrahn int
apmkqfilter(dev_t dev,struct knote * kn)315093da1aaSdrahn apmkqfilter(dev_t dev, struct knote *kn)
316d9076e62Sdrahn {
317d9076e62Sdrahn struct apm_softc *sc;
318d9076e62Sdrahn
319d9076e62Sdrahn /* apm0 only */
320d9076e62Sdrahn if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
321d9076e62Sdrahn !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
322d9076e62Sdrahn return ENXIO;
323d9076e62Sdrahn
324d9076e62Sdrahn switch (kn->kn_filter) {
325d9076e62Sdrahn case EVFILT_READ:
326d9076e62Sdrahn kn->kn_fop = &apmread_filtops;
327d9076e62Sdrahn break;
328d9076e62Sdrahn default:
329b8d5a5fbSnicm return (EINVAL);
330d9076e62Sdrahn }
331d9076e62Sdrahn
332d9076e62Sdrahn kn->kn_hook = (caddr_t)sc;
3339b0cf67bSvisa klist_insert_locked(&sc->sc_note, kn);
334d9076e62Sdrahn
335d9076e62Sdrahn return (0);
336d9076e62Sdrahn }
337e1938dd5Sgkoehler
338e1938dd5Sgkoehler #ifdef SUSPEND
3392da6f5a3Sderaadt
3408cae9a59Stobhe int
request_sleep(int sleepmode)3418cae9a59Stobhe request_sleep(int sleepmode)
3428cae9a59Stobhe {
3438cae9a59Stobhe return EOPNOTSUPP;
3448cae9a59Stobhe }
3458cae9a59Stobhe
3462da6f5a3Sderaadt #ifdef MULTIPROCESSOR
3472da6f5a3Sderaadt
3482da6f5a3Sderaadt void
sleep_mp(void)3492da6f5a3Sderaadt sleep_mp(void)
3502da6f5a3Sderaadt {
3512da6f5a3Sderaadt }
3522da6f5a3Sderaadt
3532da6f5a3Sderaadt void
resume_mp(void)3542da6f5a3Sderaadt resume_mp(void)
3552da6f5a3Sderaadt {
3562da6f5a3Sderaadt }
3572da6f5a3Sderaadt
3582da6f5a3Sderaadt #endif /* MULTIPROCESSOR */
3592da6f5a3Sderaadt
360e1938dd5Sgkoehler int
sleep_showstate(void * v,int sleepmode)361e1938dd5Sgkoehler sleep_showstate(void *v, int sleepmode)
362e1938dd5Sgkoehler {
363e1938dd5Sgkoehler switch (sleepmode) {
364e1938dd5Sgkoehler case SLEEP_SUSPEND:
365e1938dd5Sgkoehler return 0;
366e1938dd5Sgkoehler default:
367e1938dd5Sgkoehler return EOPNOTSUPP;
368e1938dd5Sgkoehler }
369e1938dd5Sgkoehler }
370e1938dd5Sgkoehler
371e1938dd5Sgkoehler int
sleep_setstate(void * v)372e1938dd5Sgkoehler sleep_setstate(void *v)
373e1938dd5Sgkoehler {
374e1938dd5Sgkoehler return 0;
375e1938dd5Sgkoehler }
376e1938dd5Sgkoehler
377e1938dd5Sgkoehler int
sleep_resume(void * v)378e1938dd5Sgkoehler sleep_resume(void *v)
379e1938dd5Sgkoehler {
380e1938dd5Sgkoehler return 0;
381e1938dd5Sgkoehler }
382e1938dd5Sgkoehler
3834a9a38d5Sderaadt int
gosleep(void * v)384e1938dd5Sgkoehler gosleep(void *v)
385e1938dd5Sgkoehler {
3864a9a38d5Sderaadt return EOPNOTSUPP;
387e1938dd5Sgkoehler }
388e1938dd5Sgkoehler
3894a9a38d5Sderaadt int
suspend_finish(void * v)390e1938dd5Sgkoehler suspend_finish(void *v)
391e1938dd5Sgkoehler {
3924a9a38d5Sderaadt return 0;
393e1938dd5Sgkoehler }
394e1938dd5Sgkoehler
395e1938dd5Sgkoehler #endif /* SUSPEND */
396