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