apm.c revision 1.4 1 /* $NetBSD: apm.c,v 1.4 2002/10/02 05:30:39 thorpej Exp $ */
2 /* $OpenBSD: apm.c,v 1.5 2002/06/07 07:13:59 miod Exp $ */
3
4 /*-
5 * Copyright (c) 2001 Alexander Guy. All rights reserved.
6 * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
7 * Copyright (c) 1995 John T. Kohl. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the University of
20 * California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 */
38
39 #include "apm.h"
40
41 #if NAPM > 1
42 #error only one APM emulation device may be configured
43 #endif
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/proc.h>
49 #include <sys/device.h>
50 #include <sys/fcntl.h>
51 #include <sys/ioctl.h>
52 #ifdef __OpenBSD__
53 #include <sys/event.h>
54 #endif
55 #ifdef __NetBSD__
56 #include <sys/select.h>
57 #include <sys/poll.h>
58 #include <sys/conf.h>
59 #endif
60
61 #ifdef __OpenBSD__
62 #include <machine/conf.h>
63 #endif
64 #include <machine/cpu.h>
65 #include <machine/apmvar.h>
66
67 #include <macppc/dev/adbvar.h>
68 #include <macppc/dev/adb_direct.h>
69 #include <macppc/dev/pm_direct.h>
70
71 #if defined(APMDEBUG)
72 #define DPRINTF(x) printf x
73 #else
74 #define DPRINTF(x) /**/
75 #endif
76
77 #define APM_NEVENTS 16
78
79 struct apm_softc {
80 struct device sc_dev;
81 struct selinfo sc_rsel;
82 #ifdef __OpenBSD__
83 struct klist sc_note;
84 #endif
85 int sc_flags;
86 int event_count;
87 int event_ptr;
88 struct lock sc_lock;
89 struct apm_event_info event_list[APM_NEVENTS];
90 };
91
92 /*
93 * A brief note on the locking protocol: it's very simple; we
94 * assert an exclusive lock any time thread context enters the
95 * APM module. This is both the APM thread itself, as well as
96 * user context.
97 */
98 #ifdef __NetBSD__
99 #define APM_LOCK(apmsc) \
100 (void) lockmgr(&(apmsc)->sc_lock, LK_EXCLUSIVE, NULL)
101 #define APM_UNLOCK(apmsc) \
102 (void) lockmgr(&(apmsc)->sc_lock, LK_RELEASE, NULL)
103 #else
104 #define APM_LOCK(apmsc)
105 #define APM_UNLOCK(apmsc)
106 #endif
107
108 int apmmatch(struct device *, struct cfdata *, void *);
109 void apmattach(struct device *, struct device *, void *);
110
111 #ifdef __NetBSD__
112 #if 0
113 static int apm_record_event __P((struct apm_softc *, u_int));
114 #endif
115 #endif
116
117 CFATTACH_DECL(apm, sizeof(struct apm_softc),
118 apmmatch, apmattach, NULL, NULL);
119
120 #ifdef __OpenBSD__
121 struct cfdriver apm_cd = {
122 NULL, "apm", DV_DULL
123 };
124 #else
125 extern struct cfdriver apm_cd;
126
127 dev_type_open(apmopen);
128 dev_type_close(apmclose);
129 dev_type_ioctl(apmioctl);
130 dev_type_poll(apmpoll);
131
132 const struct cdevsw apm_cdevsw = {
133 apmopen, apmclose, noread, nowrite, apmioctl,
134 nostop, notty, apmpoll, nommap,
135 };
136 #endif
137
138 int apm_evindex;
139
140 #define APMUNIT(dev) (minor(dev)&0xf0)
141 #define APMDEV(dev) (minor(dev)&0x0f)
142 #define APMDEV_NORMAL 0
143 #define APMDEV_CTL 8
144
145 #ifdef __OpenBSD__
146 void filt_apmrdetach(struct knote *kn);
147 int filt_apmread(struct knote *kn, long hint);
148 int apmkqfilter(dev_t dev, struct knote *kn);
149
150 struct filterops apmread_filtops =
151 { 1, NULL, filt_apmrdetach, filt_apmread};
152 #endif
153
154 /*
155 * Flags to control kernel display
156 * SCFLAG_NOPRINT: do not output APM power messages due to
157 * a power change event.
158 *
159 * SCFLAG_PCTPRINT: do not output APM power messages due to
160 * to a power change event unless the battery
161 * percentage changes.
162 */
163
164 #define SCFLAG_NOPRINT 0x0008000
165 #define SCFLAG_PCTPRINT 0x0004000
166 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
167
168 #define SCFLAG_OREAD (1 << 0)
169 #define SCFLAG_OWRITE (1 << 1)
170 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
171
172
173 int
174 apmmatch(parent, match, aux)
175 struct device *parent;
176 struct cfdata *match;
177 void *aux;
178 {
179 struct adb_attach_args *aa = (void *)aux;
180 if (aa->origaddr != ADBADDR_APM ||
181 aa->handler_id != ADBADDR_APM ||
182 aa->adbaddr != ADBADDR_APM)
183 return 0;
184
185 if (adbHardware != ADB_HW_PB)
186 return 0;
187
188 return 1;
189 }
190
191 void
192 apmattach(parent, self, aux)
193 struct device *parent, *self;
194 void *aux;
195 {
196 struct pmu_battery_info info;
197
198 pm_battery_info(0, &info);
199
200 printf(": battery flags 0x%X, ", info.flags);
201 printf("%d%% charged\n", ((info.cur_charge * 100) / info.max_charge));
202 }
203
204 int
205 apmopen(dev, flag, mode, p)
206 dev_t dev;
207 int flag, mode;
208 struct proc *p;
209 {
210 struct apm_softc *sc;
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 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
219 APMDEV(dev), p->p_pid, flag, mode));
220
221 APM_LOCK(sc);
222 switch (APMDEV(dev)) {
223 case APMDEV_CTL:
224 if (!(flag & FWRITE)) {
225 error = EINVAL;
226 break;
227 }
228 if (sc->sc_flags & SCFLAG_OWRITE) {
229 error = EBUSY;
230 break;
231 }
232 sc->sc_flags |= SCFLAG_OWRITE;
233 break;
234 case APMDEV_NORMAL:
235 if (!(flag & FREAD) || (flag & FWRITE)) {
236 error = EINVAL;
237 break;
238 }
239 sc->sc_flags |= SCFLAG_OREAD;
240 break;
241 default:
242 error = ENXIO;
243 break;
244 }
245 APM_UNLOCK(sc);
246 return error;
247 }
248
249 int
250 apmclose(dev, flag, mode, p)
251 dev_t dev;
252 int flag, mode;
253 struct proc *p;
254 {
255 struct apm_softc *sc;
256
257 /* apm0 only */
258 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
259 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
260 return ENXIO;
261
262 DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
263
264 APM_LOCK(sc);
265 switch (APMDEV(dev)) {
266 case APMDEV_CTL:
267 sc->sc_flags &= ~SCFLAG_OWRITE;
268 break;
269 case APMDEV_NORMAL:
270 sc->sc_flags &= ~SCFLAG_OREAD;
271 break;
272 }
273 APM_UNLOCK(sc);
274 return 0;
275 }
276
277 int
278 apmioctl(dev, cmd, data, flag, p)
279 dev_t dev;
280 u_long cmd;
281 caddr_t data;
282 int flag;
283 struct proc *p;
284 {
285 struct apm_softc *sc;
286 struct pmu_battery_info batt;
287 struct apm_power_info *power;
288 int error = 0;
289
290 /* apm0 only */
291 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
292 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
293 return ENXIO;
294
295 APM_LOCK(sc);
296 switch (cmd) {
297 /* some ioctl names from linux */
298 case APM_IOC_STANDBY:
299 if ((flag & FWRITE) == 0)
300 error = EBADF;
301 case APM_IOC_SUSPEND:
302 if ((flag & FWRITE) == 0)
303 error = EBADF;
304 break;
305 case APM_IOC_PRN_CTL:
306 if ((flag & FWRITE) == 0)
307 error = EBADF;
308 else {
309 int flag = *(int *)data;
310 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
311 switch (flag) {
312 case APM_PRINT_ON: /* enable printing */
313 sc->sc_flags &= ~SCFLAG_PRINT;
314 break;
315 case APM_PRINT_OFF: /* disable printing */
316 sc->sc_flags &= ~SCFLAG_PRINT;
317 sc->sc_flags |= SCFLAG_NOPRINT;
318 break;
319 case APM_PRINT_PCT: /* disable some printing */
320 sc->sc_flags &= ~SCFLAG_PRINT;
321 sc->sc_flags |= SCFLAG_PCTPRINT;
322 break;
323 default:
324 error = EINVAL;
325 break;
326 }
327 }
328 break;
329 case APM_IOC_DEV_CTL:
330 if ((flag & FWRITE) == 0)
331 error = EBADF;
332 break;
333 case APM_IOC_GETPOWER:
334 power = (struct apm_power_info *)data;
335
336 pm_battery_info(0, &batt);
337
338 power->ac_state = ((batt.flags & PMU_PWR_AC_PRESENT) ?
339 APM_AC_ON : APM_AC_OFF);
340 power->battery_life =
341 ((batt.cur_charge * 100) / batt.max_charge);
342
343 /*
344 * If the battery is charging, return the minutes left until
345 * charging is complete. apmd knows this.
346 */
347
348 if (!(batt.flags & PMU_PWR_BATT_PRESENT)) {
349 power->battery_state = APM_BATT_UNKNOWN;
350 power->minutes_left = 0;
351 power->battery_life = 0;
352 } else if ((power->ac_state == APM_AC_ON) &&
353 (batt.draw > 0)) {
354 power->minutes_left =
355 (((batt.max_charge - batt.cur_charge) * 3600) /
356 batt.draw) / 60;
357 power->battery_state = APM_BATT_CHARGING;
358 } else {
359 power->minutes_left =
360 ((batt.cur_charge * 3600) / (-batt.draw)) / 60;
361
362 /* XXX - Arbitrary */
363 if (power->battery_life > 60) {
364 power->battery_state = APM_BATT_HIGH;
365 } else if (power->battery_life < 10) {
366 power->battery_state = APM_BATT_CRITICAL;
367 } else {
368 power->battery_state = APM_BATT_LOW;
369 }
370 }
371
372 break;
373
374 default:
375 error = ENOTTY;
376 }
377 APM_UNLOCK(sc);
378
379 return error;
380 }
381
382 #ifdef __NetBSD__
383 #if 0
384 /*
385 * return 0 if the user will notice and handle the event,
386 * return 1 if the kernel driver should do so.
387 */
388 static int
389 apm_record_event(sc, event_type)
390 struct apm_softc *sc;
391 u_int event_type;
392 {
393 struct apm_event_info *evp;
394
395 if ((sc->sc_flags & SCFLAG_OPEN) == 0)
396 return 1; /* no user waiting */
397 if (sc->event_count == APM_NEVENTS) {
398 DPRINTF(("apm_record_event: queue full!\n"));
399 return 1; /* overflow */
400 }
401 evp = &sc->event_list[sc->event_ptr];
402 sc->event_count++;
403 sc->event_ptr++;
404 sc->event_ptr %= APM_NEVENTS;
405 evp->type = event_type;
406 evp->index = ++apm_evindex;
407 selwakeup(&sc->sc_rsel);
408 return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */
409 }
410 #endif
411
412 int
413 apmpoll(dev, events, p)
414 dev_t dev;
415 int events;
416 struct proc *p;
417 {
418 struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
419 int revents = 0;
420
421 APM_LOCK(sc);
422 if (events & (POLLIN | POLLRDNORM)) {
423 if (sc->event_count)
424 revents |= events & (POLLIN | POLLRDNORM);
425 else
426 selrecord(p, &sc->sc_rsel);
427 }
428 APM_UNLOCK(sc);
429
430 return (revents);
431 }
432 #endif
433
434 #ifdef __OpenBSD__
435 void
436 filt_apmrdetach(kn)
437 struct knote *kn;
438 {
439 struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
440
441 SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
442 }
443
444 int
445 filt_apmread(kn, hint)
446 struct knote *kn;
447 long hint;
448 {
449 /* XXX weird kqueue_scan() semantics */
450 if (hint && !kn->kn_data)
451 kn->kn_data = (int)hint;
452
453 return (1);
454 }
455
456 int
457 apmkqfilter(dev, kn)
458 dev_t dev;
459 struct knote *kn;
460 {
461 struct apm_softc *sc;
462
463 /* apm0 only */
464 if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
465 !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
466 return ENXIO;
467
468 switch (kn->kn_filter) {
469 case EVFILT_READ:
470 kn->kn_fop = &apmread_filtops;
471 break;
472 default:
473 return (1);
474 }
475
476 kn->kn_hook = (caddr_t)sc;
477 SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);
478
479 return (0);
480 }
481 #endif
482