1 /* $OpenBSD: apm.c,v 1.37 2024/05/29 06:39:13 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 #if NAPM > 1
37 #error only one APM emulation device may be configured
38 #endif
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/proc.h>
44 #include <sys/device.h>
45 #include <sys/fcntl.h>
46 #include <sys/ioctl.h>
47 #include <sys/event.h>
48
49 #include <machine/conf.h>
50 #include <machine/cpu.h>
51 #include <machine/apmvar.h>
52 #include <machine/autoconf.h>
53
54 #include <macppc/dev/pm_direct.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 const 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
88 const struct filterops apmread_filtops = {
89 .f_flags = FILTEROP_ISFD,
90 .f_attach = NULL,
91 .f_detach = filt_apmrdetach,
92 .f_event = filt_apmread,
93 };
94
95 /*
96 * Flags to control kernel display
97 * SCFLAG_NOPRINT: do not output APM power messages due to
98 * a power change event.
99 *
100 * SCFLAG_PCTPRINT: do not output APM power messages due to
101 * to a power change event unless the battery
102 * percentage changes.
103 */
104
105 #define SCFLAG_NOPRINT 0x0008000
106 #define SCFLAG_PCTPRINT 0x0004000
107 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
108
109 #define SCFLAG_OREAD (1 << 0)
110 #define SCFLAG_OWRITE (1 << 1)
111 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
112
113
114 int
apmmatch(struct device * parent,void * match,void * aux)115 apmmatch(struct device *parent, void *match, void *aux)
116 {
117 struct confargs *ca = aux;
118
119 if (strcmp(ca->ca_name, "apm") != 0)
120 return (0);
121
122 return (1);
123 }
124
125 void
apmattach(struct device * parent,struct device * self,void * aux)126 apmattach(struct device *parent, struct device *self, void *aux)
127 {
128 struct pmu_battery_info info;
129
130 pm_battery_info(0, &info);
131
132 printf(": battery flags 0x%X, ", info.flags);
133 printf("%d%% charged\n", ((info.cur_charge * 100) / info.max_charge));
134
135 #ifdef SUSPEND
136 device_register_wakeup(self);
137 #endif
138 }
139
140 int
apmopen(dev_t dev,int flag,int mode,struct proc * p)141 apmopen(dev_t dev, int flag, int mode, struct proc *p)
142 {
143 struct apm_softc *sc;
144 int error = 0;
145
146 /* apm0 only */
147 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
148 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
149 return ENXIO;
150
151 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
152 APMDEV(dev), p->p_p->ps_pid, flag, mode));
153
154 switch (APMDEV(dev)) {
155 case APMDEV_CTL:
156 if (!(flag & FWRITE)) {
157 error = EINVAL;
158 break;
159 }
160 if (sc->sc_flags & SCFLAG_OWRITE) {
161 error = EBUSY;
162 break;
163 }
164 sc->sc_flags |= SCFLAG_OWRITE;
165 break;
166 case APMDEV_NORMAL:
167 if (!(flag & FREAD) || (flag & FWRITE)) {
168 error = EINVAL;
169 break;
170 }
171 sc->sc_flags |= SCFLAG_OREAD;
172 break;
173 default:
174 error = ENXIO;
175 break;
176 }
177 return error;
178 }
179
180 int
apmclose(dev_t dev,int flag,int mode,struct proc * p)181 apmclose(dev_t dev, int flag, int mode, struct proc *p)
182 {
183 struct apm_softc *sc;
184
185 /* apm0 only */
186 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
187 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
188 return ENXIO;
189
190 DPRINTF(("apmclose: pid %d flag %x mode %x\n",
191 p->p_p->ps_pid, flag, mode));
192
193 switch (APMDEV(dev)) {
194 case APMDEV_CTL:
195 sc->sc_flags &= ~SCFLAG_OWRITE;
196 break;
197 case APMDEV_NORMAL:
198 sc->sc_flags &= ~SCFLAG_OREAD;
199 break;
200 }
201 return 0;
202 }
203
204 int
apmioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)205 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
206 {
207 struct apm_softc *sc;
208 struct pmu_battery_info batt;
209 struct apm_power_info *power;
210 int error = 0;
211
212 /* apm0 only */
213 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
214 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
215 return ENXIO;
216
217 switch (cmd) {
218 #ifdef SUSPEND
219 case APM_IOC_STANDBY:
220 case APM_IOC_SUSPEND:
221 if ((flag & FWRITE) == 0) {
222 error = EBADF;
223 break;
224 }
225 sleep_state(sc, SLEEP_SUSPEND);
226 break;
227 #endif
228 case APM_IOC_PRN_CTL:
229 if ((flag & FWRITE) == 0)
230 error = EBADF;
231 else {
232 int flag = *(int *)data;
233 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
234 switch (flag) {
235 case APM_PRINT_ON: /* enable printing */
236 sc->sc_flags &= ~SCFLAG_PRINT;
237 break;
238 case APM_PRINT_OFF: /* disable printing */
239 sc->sc_flags &= ~SCFLAG_PRINT;
240 sc->sc_flags |= SCFLAG_NOPRINT;
241 break;
242 case APM_PRINT_PCT: /* disable some printing */
243 sc->sc_flags &= ~SCFLAG_PRINT;
244 sc->sc_flags |= SCFLAG_PCTPRINT;
245 break;
246 default:
247 error = EINVAL;
248 break;
249 }
250 }
251 break;
252 case APM_IOC_GETPOWER:
253 power = (struct apm_power_info *)data;
254
255 pm_battery_info(0, &batt);
256
257 power->ac_state = ((batt.flags & PMU_PWR_AC_PRESENT) ?
258 APM_AC_ON : APM_AC_OFF);
259 power->battery_life =
260 ((batt.cur_charge * 100) / batt.max_charge);
261
262 /*
263 * If the battery is charging, return the minutes left until
264 * charging is complete. apmd knows this.
265 */
266
267 if (!(batt.flags & PMU_PWR_BATT_PRESENT)) {
268 power->battery_state = APM_BATT_UNKNOWN;
269 power->minutes_left = 0;
270 power->battery_life = 0;
271 } else if ((power->ac_state == APM_AC_ON) &&
272 (batt.draw > 0)) {
273 power->minutes_left =
274 (((batt.max_charge - batt.cur_charge) * 3600) /
275 batt.draw) / 60;
276 power->battery_state = APM_BATT_CHARGING;
277 } else {
278 power->minutes_left =
279 ((batt.cur_charge * 3600) / (-batt.draw)) / 60;
280
281 if (power->battery_life > 50)
282 power->battery_state = APM_BATT_HIGH;
283 else if (power->battery_life > 25)
284 power->battery_state = APM_BATT_LOW;
285 else
286 power->battery_state = APM_BATT_CRITICAL;
287 }
288 break;
289 default:
290 error = ENOTTY;
291 }
292
293 return error;
294 }
295
296 void
filt_apmrdetach(struct knote * kn)297 filt_apmrdetach(struct knote *kn)
298 {
299 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
300
301 klist_remove_locked(&sc->sc_note, kn);
302 }
303
304 int
filt_apmread(struct knote * kn,long hint)305 filt_apmread(struct knote *kn, long hint)
306 {
307 /* XXX weird kqueue_scan() semantics */
308 if (hint && !kn->kn_data)
309 kn->kn_data = (int)hint;
310
311 return (1);
312 }
313
314 int
apmkqfilter(dev_t dev,struct knote * kn)315 apmkqfilter(dev_t dev, struct knote *kn)
316 {
317 struct apm_softc *sc;
318
319 /* apm0 only */
320 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
321 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
322 return ENXIO;
323
324 switch (kn->kn_filter) {
325 case EVFILT_READ:
326 kn->kn_fop = &apmread_filtops;
327 break;
328 default:
329 return (EINVAL);
330 }
331
332 kn->kn_hook = (caddr_t)sc;
333 klist_insert_locked(&sc->sc_note, kn);
334
335 return (0);
336 }
337
338 #ifdef SUSPEND
339
340 int
request_sleep(int sleepmode)341 request_sleep(int sleepmode)
342 {
343 return EOPNOTSUPP;
344 }
345
346 #ifdef MULTIPROCESSOR
347
348 void
sleep_mp(void)349 sleep_mp(void)
350 {
351 }
352
353 void
resume_mp(void)354 resume_mp(void)
355 {
356 }
357
358 #endif /* MULTIPROCESSOR */
359
360 int
sleep_showstate(void * v,int sleepmode)361 sleep_showstate(void *v, int sleepmode)
362 {
363 switch (sleepmode) {
364 case SLEEP_SUSPEND:
365 return 0;
366 default:
367 return EOPNOTSUPP;
368 }
369 }
370
371 int
sleep_setstate(void * v)372 sleep_setstate(void *v)
373 {
374 return 0;
375 }
376
377 int
sleep_resume(void * v)378 sleep_resume(void *v)
379 {
380 return 0;
381 }
382
383 int
gosleep(void * v)384 gosleep(void *v)
385 {
386 return EOPNOTSUPP;
387 }
388
389 int
suspend_finish(void * v)390 suspend_finish(void *v)
391 {
392 return 0;
393 }
394
395 #endif /* SUSPEND */
396