xref: /netbsd-src/sys/dev/hpc/apm/apmdev.c (revision f31bd063e99a4029f1bf9dac360c83c605ab8ba4)
1 /*	$NetBSD: apmdev.c,v 1.2 2005/02/27 00:27:00 perry Exp $ */
2 
3 /*-
4  * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by John Kohl and Christopher G. Demetriou.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 /*
39  * from: sys/arch/i386/i386/apm.c,v 1.49 2000/05/08
40  */
41 
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: apmdev.c,v 1.2 2005/02/27 00:27:00 perry Exp $");
44 
45 #include "opt_apmdev.h"
46 
47 #ifdef APM_NOIDLE
48 #error APM_NOIDLE option deprecated; use APM_NO_IDLE instead
49 #endif
50 
51 #if defined(DEBUG) && !defined(APMDEBUG)
52 #define	APMDEBUG
53 #endif
54 
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/signalvar.h>
58 #include <sys/kernel.h>
59 #include <sys/proc.h>
60 #include <sys/kthread.h>
61 #include <sys/lock.h>
62 #include <sys/user.h>
63 #include <sys/malloc.h>
64 #include <sys/device.h>
65 #include <sys/fcntl.h>
66 #include <sys/ioctl.h>
67 #include <sys/select.h>
68 #include <sys/poll.h>
69 #include <sys/conf.h>
70 
71 #include <dev/hpc/apm/apmvar.h>
72 
73 #include <machine/stdarg.h>
74 
75 #define	APMDEBUG
76 #if defined(APMDEBUG)
77 #define	DPRINTF(f, x)		do { if (apmdebug & (f)) printf x; } while (0)
78 
79 #define	APMDEBUG_INFO		0x01
80 #define	APMDEBUG_APMCALLS	0x02
81 #define	APMDEBUG_EVENTS		0x04
82 #define	APMDEBUG_PROBE		0x10
83 #define	APMDEBUG_ATTACH		0x40
84 #define	APMDEBUG_DEVICE		0x20
85 #define	APMDEBUG_ANOM		0x40
86 
87 #ifdef APMDEBUG_VALUE
88 int	apmdebug = APMDEBUG_VALUE;
89 #else
90 int	apmdebug = 0;
91 #endif
92 #else
93 #define	DPRINTF(f, x)		/**/
94 #endif
95 
96 #define APM_NEVENTS 16
97 
98 struct apm_softc {
99 	struct device sc_dev;
100 	struct selinfo sc_rsel;
101 	struct selinfo sc_xsel;
102 	int	sc_flags;
103 	int	event_count;
104 	int	event_ptr;
105 	int	sc_power_state;
106 	struct proc *sc_thread;
107 	struct lock sc_lock;
108 	struct apm_event_info event_list[APM_NEVENTS];
109 	struct apm_accessops *ops;
110 	void *cookie;
111 };
112 #define	SCFLAG_OREAD	0x0000001
113 #define	SCFLAG_OWRITE	0x0000002
114 #define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)
115 
116 #define	APMUNIT(dev)	(minor(dev)&0xf0)
117 #define	APMDEV(dev)	(minor(dev)&0x0f)
118 #define APMDEV_NORMAL	0
119 #define APMDEV_CTL	8
120 
121 /*
122  * A brief note on the locking protocol: it's very simple; we
123  * assert an exclusive lock any time thread context enters the
124  * APM module.  This is both the APM thread itself, as well as
125  * user context.
126  */
127 #define	APM_LOCK(apmsc)							\
128 	(void) lockmgr(&(apmsc)->sc_lock, LK_EXCLUSIVE, NULL)
129 #define	APM_UNLOCK(apmsc)						\
130 	(void) lockmgr(&(apmsc)->sc_lock, LK_RELEASE, NULL)
131 
132 static void	apmattach(struct device *, struct device *, void *);
133 static int	apmmatch(struct device *, struct cfdata *, void *);
134 
135 static void	apm_event_handle(struct apm_softc *, u_int, u_int);
136 static void	apm_periodic_check(struct apm_softc *);
137 static void	apm_create_thread(void *);
138 static void	apm_thread(void *);
139 static void	apm_perror(const char *, int, ...)
140 		    __attribute__((__format__(__printf__,1,3)));
141 #ifdef APM_POWER_PRINT
142 static void	apm_power_print(struct apm_softc *, struct apm_power_info *);
143 #endif
144 static int	apm_record_event(struct apm_softc *, u_int);
145 static void	apm_set_ver(struct apm_softc *, u_long);
146 static void	apm_standby(struct apm_softc *);
147 static const char *apm_strerror(int);
148 static void	apm_suspend(struct apm_softc *);
149 static void	apm_resume(struct apm_softc *, u_int, u_int);
150 
151 CFATTACH_DECL(apmdev, sizeof(struct apm_softc),
152     apmmatch, apmattach, NULL, NULL);
153 
154 extern struct cfdriver apmdev_cd;
155 
156 dev_type_open(apmdevopen);
157 dev_type_close(apmdevclose);
158 dev_type_ioctl(apmdevioctl);
159 dev_type_poll(apmdevpoll);
160 dev_type_kqfilter(apmdevkqfilter);
161 
162 const struct cdevsw apmdev_cdevsw = {
163 	apmdevopen, apmdevclose, noread, nowrite, apmdevioctl,
164 	nostop, notty, apmdevpoll, nommap, apmdevkqfilter,
165 };
166 
167 /* configurable variables */
168 int	apm_bogus_bios = 0;
169 #ifdef APM_DISABLE
170 int	apm_enabled = 0;
171 #else
172 int	apm_enabled = 1;
173 #endif
174 #ifdef APM_NO_IDLE
175 int	apm_do_idle = 0;
176 #else
177 int	apm_do_idle = 1;
178 #endif
179 #ifdef APM_NO_STANDBY
180 int	apm_do_standby = 0;
181 #else
182 int	apm_do_standby = 1;
183 #endif
184 #ifdef APM_V10_ONLY
185 int	apm_v11_enabled = 0;
186 #else
187 int	apm_v11_enabled = 1;
188 #endif
189 #ifdef APM_NO_V12
190 int	apm_v12_enabled = 0;
191 #else
192 int	apm_v12_enabled = 1;
193 #endif
194 
195 /* variables used during operation (XXX cgd) */
196 u_char	apm_majver, apm_minver;
197 int	apm_inited;
198 int	apm_standbys, apm_userstandbys, apm_suspends, apm_battlow;
199 int	apm_damn_fool_bios, apm_op_inprog;
200 int	apm_evindex;
201 
202 static int apm_spl;		/* saved spl while suspended */
203 
204 static const char *
205 apm_strerror(int code)
206 {
207 	switch (code) {
208 	case APM_ERR_PM_DISABLED:
209 		return ("power management disabled");
210 	case APM_ERR_REALALREADY:
211 		return ("real mode interface already connected");
212 	case APM_ERR_NOTCONN:
213 		return ("interface not connected");
214 	case APM_ERR_16ALREADY:
215 		return ("16-bit interface already connected");
216 	case APM_ERR_16NOTSUPP:
217 		return ("16-bit interface not supported");
218 	case APM_ERR_32ALREADY:
219 		return ("32-bit interface already connected");
220 	case APM_ERR_32NOTSUPP:
221 		return ("32-bit interface not supported");
222 	case APM_ERR_UNRECOG_DEV:
223 		return ("unrecognized device ID");
224 	case APM_ERR_ERANGE:
225 		return ("parameter out of range");
226 	case APM_ERR_NOTENGAGED:
227 		return ("interface not engaged");
228 	case APM_ERR_UNABLE:
229 		return ("unable to enter requested state");
230 	case APM_ERR_NOEVENTS:
231 		return ("no pending events");
232 	case APM_ERR_NOT_PRESENT:
233 		return ("no APM present");
234 	default:
235 		return ("unknown error code");
236 	}
237 }
238 
239 static void
240 apm_perror(const char *str, int errinfo, ...) /* XXX cgd */
241 {
242 	va_list ap;
243 
244 	printf("APM ");
245 
246 	va_start(ap, errinfo);
247 	vprintf(str, ap);			/* XXX cgd */
248 	va_end(ap);
249 
250 	printf(": %s\n", apm_strerror(errinfo));
251 }
252 
253 #ifdef APM_POWER_PRINT
254 static void
255 apm_power_print(struct apm_softc *sc, struct apm_power_info *pi)
256 {
257 
258 	if (pi->battery_life != APM_BATT_LIFE_UNKNOWN) {
259 		printf("%s: battery life expectancy: %d%%\n",
260 		    sc->sc_dev.dv_xname, pi->battery_life);
261 	}
262 	printf("%s: A/C state: ", sc->sc_dev.dv_xname);
263 	switch (pi->ac_state) {
264 	case APM_AC_OFF:
265 		printf("off\n");
266 		break;
267 	case APM_AC_ON:
268 		printf("on\n");
269 		break;
270 	case APM_AC_BACKUP:
271 		printf("backup power\n");
272 		break;
273 	default:
274 	case APM_AC_UNKNOWN:
275 		printf("unknown\n");
276 		break;
277 	}
278 	if (apm_major == 1 && apm_minor == 0) {
279 		printf("%s: battery charge state:", sc->sc_dev.dv_xname);
280 		switch (pi->battery_state) {
281 		case APM_BATT_HIGH:
282 			printf("high\n");
283 			break;
284 		case APM_BATT_LOW:
285 			printf("low\n");
286 			break;
287 		case APM_BATT_CRITICAL:
288 			printf("critical\n");
289 			break;
290 		case APM_BATT_CHARGING:
291 			printf("charging\n");
292 			break;
293 		case APM_BATT_UNKNOWN:
294 			printf("unknown\n");
295 			break;
296 		default:
297 			printf("undecoded state %x\n", pi->battery_state);
298 			break;
299 		}
300 	} else {
301 		if (pi->battery_state&APM_BATT_FLAG_CHARGING)
302 			printf("charging ");
303 		}
304 		if (pi->battery_state&APM_BATT_FLAG_UNKNOWN)
305 			printf("unknown\n");
306 		else if (pi->battery_state&APM_BATT_FLAG_CRITICAL)
307 			printf("critical\n");
308 		else if (pi->battery_state&APM_BATT_FLAG_LOW)
309 			printf("low\n");
310 		else if (pi->battery_state&APM_BATT_FLAG_HIGH)
311 			printf("high\n");
312 	}
313 	if (pi->minutes_left != 0) {
314 		printf("%s: estimated ", sc->sc_dev.dv_xname);
315 		printf("%dh ", pi->minutes_left / 60);
316 	}
317 	return;
318 }
319 #endif
320 
321 static void
322 apm_suspend(struct apm_softc *sc)
323 {
324 
325 	if (sc->sc_power_state == PWR_SUSPEND) {
326 #ifdef APMDEBUG
327 		printf("%s: apm_suspend: already suspended?\n",
328 		    sc->sc_dev.dv_xname);
329 #endif
330 		return;
331 	}
332 	sc->sc_power_state = PWR_SUSPEND;
333 
334 	dopowerhooks(PWR_SOFTSUSPEND);
335 	(void) tsleep(sc, PWAIT, "apmsuspend",  hz/2);
336 
337 	apm_spl = splhigh();
338 
339 	dopowerhooks(PWR_SUSPEND);
340 
341 	/* XXX cgd */
342 	(void)sc->ops->set_powstate(sc->cookie, APM_DEV_ALLDEVS, APM_SYS_SUSPEND);
343 }
344 
345 static void
346 apm_standby(struct apm_softc *sc)
347 {
348 
349 	if (sc->sc_power_state == PWR_STANDBY) {
350 #ifdef APMDEBUG
351 		printf("%s: apm_standby: already standing by?\n",
352 		    sc->sc_dev.dv_xname);
353 #endif
354 		return;
355 	}
356 	sc->sc_power_state = PWR_STANDBY;
357 
358 	dopowerhooks(PWR_SOFTSTANDBY);
359 	(void) tsleep(sc, PWAIT, "apmstandby",  hz/2);
360 
361 	apm_spl = splhigh();
362 
363 	dopowerhooks(PWR_STANDBY);
364 	/* XXX cgd */
365 	(void)sc->ops->set_powstate(sc->cookie, APM_DEV_ALLDEVS, APM_SYS_STANDBY);
366 }
367 
368 static void
369 apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info)
370 {
371 
372 	if (sc->sc_power_state == PWR_RESUME) {
373 #ifdef APMDEBUG
374 		printf("%s: apm_resume: already running?\n",
375 		    sc->sc_dev.dv_xname);
376 #endif
377 		return;
378 	}
379 	sc->sc_power_state = PWR_RESUME;
380 
381 	/*
382 	 * Some system requires its clock to be initialized after hybernation.
383 	 */
384 /* XXX
385 	initrtclock();
386 */
387 
388 	inittodr(time.tv_sec);
389 	dopowerhooks(PWR_RESUME);
390 
391 	splx(apm_spl);
392 
393 	dopowerhooks(PWR_SOFTRESUME);
394 
395 	apm_record_event(sc, event_type);
396 }
397 
398 /*
399  * return 0 if the user will notice and handle the event,
400  * return 1 if the kernel driver should do so.
401  */
402 static int
403 apm_record_event(struct apm_softc *sc, u_int event_type)
404 {
405 	struct apm_event_info *evp;
406 
407 	if ((sc->sc_flags & SCFLAG_OPEN) == 0)
408 		return 1;		/* no user waiting */
409 	if (sc->event_count == APM_NEVENTS)
410 		return 1;			/* overflow */
411 	evp = &sc->event_list[sc->event_ptr];
412 	sc->event_count++;
413 	sc->event_ptr++;
414 	sc->event_ptr %= APM_NEVENTS;
415 	evp->type = event_type;
416 	evp->index = ++apm_evindex;
417 	selnotify(&sc->sc_rsel, 0);
418 	return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */
419 }
420 
421 static void
422 apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info)
423 {
424 	int error;
425 	char *code;
426 	struct apm_power_info pi;
427 
428 	switch (event_code) {
429 	case APM_USER_STANDBY_REQ:
430 		DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n"));
431 		if (apm_do_standby) {
432 			if (apm_record_event(sc, event_code))
433 				apm_userstandbys++;
434 			apm_op_inprog++;
435 			(void)sc->ops->set_powstate(sc->cookie,
436 						    APM_DEV_ALLDEVS,
437 						    APM_LASTREQ_INPROG);
438 		} else {
439 			(void)sc->ops->set_powstate(sc->cookie,
440 						    APM_DEV_ALLDEVS,
441 						    APM_LASTREQ_REJECTED);
442 			/* in case BIOS hates being spurned */
443 			sc->ops->enable(sc->cookie, 1);
444 		}
445 		break;
446 
447 	case APM_STANDBY_REQ:
448 		DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n"));
449 		if (apm_standbys || apm_suspends) {
450 			DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
451 			    ("damn fool BIOS did not wait for answer\n"));
452 			/* just give up the fight */
453 			apm_damn_fool_bios = 1;
454 		}
455 		if (apm_do_standby) {
456 			if (apm_record_event(sc, event_code))
457 				apm_standbys++;
458 			apm_op_inprog++;
459 			(void)sc->ops->set_powstate(sc->cookie,
460 						    APM_DEV_ALLDEVS,
461 						    APM_LASTREQ_INPROG);
462 		} else {
463 			(void)sc->ops->set_powstate(sc->cookie,
464 						    APM_DEV_ALLDEVS,
465 						    APM_LASTREQ_REJECTED);
466 			/* in case BIOS hates being spurned */
467 			sc->ops->enable(sc->cookie, 1);
468 		}
469 		break;
470 
471 	case APM_USER_SUSPEND_REQ:
472 		DPRINTF(APMDEBUG_EVENTS, ("apmev: user suspend request\n"));
473 		if (apm_record_event(sc, event_code))
474 			apm_suspends++;
475 		apm_op_inprog++;
476 		(void)sc->ops->set_powstate(sc->cookie,
477 					    APM_DEV_ALLDEVS,
478 					    APM_LASTREQ_INPROG);
479 		break;
480 
481 	case APM_SUSPEND_REQ:
482 		DPRINTF(APMDEBUG_EVENTS, ("apmev: system suspend request\n"));
483 		if (apm_standbys || apm_suspends) {
484 			DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
485 			    ("damn fool BIOS did not wait for answer\n"));
486 			/* just give up the fight */
487 			apm_damn_fool_bios = 1;
488 		}
489 		if (apm_record_event(sc, event_code))
490 			apm_suspends++;
491 		apm_op_inprog++;
492 		(void)sc->ops->set_powstate(sc->cookie,
493 					    APM_DEV_ALLDEVS,
494 					    APM_LASTREQ_INPROG);
495 		break;
496 
497 	case APM_POWER_CHANGE:
498 		DPRINTF(APMDEBUG_EVENTS, ("apmev: power status change\n"));
499 		error = sc->ops->get_powstat(sc->cookie, &pi);
500 #ifdef APM_POWER_PRINT
501 		/* only print if nobody is catching events. */
502 		if (error == 0 &&
503 		    (sc->sc_flags & (SCFLAG_OREAD|SCFLAG_OWRITE)) == 0)
504 			apm_power_print(sc, &pi);
505 #endif
506 		apm_record_event(sc, event_code);
507 		break;
508 
509 	case APM_NORMAL_RESUME:
510 		DPRINTF(APMDEBUG_EVENTS, ("apmev: resume system\n"));
511 		apm_resume(sc, event_code, event_info);
512 		break;
513 
514 	case APM_CRIT_RESUME:
515 		DPRINTF(APMDEBUG_EVENTS, ("apmev: critical resume system"));
516 		apm_resume(sc, event_code, event_info);
517 		break;
518 
519 	case APM_SYS_STANDBY_RESUME:
520 		DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby resume\n"));
521 		apm_resume(sc, event_code, event_info);
522 		break;
523 
524 	case APM_UPDATE_TIME:
525 		DPRINTF(APMDEBUG_EVENTS, ("apmev: update time\n"));
526 		apm_resume(sc, event_code, event_info);
527 		break;
528 
529 	case APM_CRIT_SUSPEND_REQ:
530 		DPRINTF(APMDEBUG_EVENTS, ("apmev: critical system suspend\n"));
531 		apm_record_event(sc, event_code);
532 		apm_suspend(sc);
533 		break;
534 
535 	case APM_BATTERY_LOW:
536 		DPRINTF(APMDEBUG_EVENTS, ("apmev: battery low\n"));
537 		apm_battlow++;
538 		apm_record_event(sc, event_code);
539 		break;
540 
541 	case APM_CAP_CHANGE:
542 		DPRINTF(APMDEBUG_EVENTS, ("apmev: capability change\n"));
543 		if (apm_minver < 2) {
544 			DPRINTF(APMDEBUG_EVENTS, ("apm: unexpected event\n"));
545 		} else {
546 			u_int numbatts, capflags;
547 			sc->ops->get_capabilities(sc->cookie,
548 						  &numbatts, &capflags);
549 			sc->ops->get_powstat(sc->cookie, &pi); /* XXX */
550 		}
551 		break;
552 
553 	default:
554 		switch (event_code >> 8) {
555 			case 0:
556 				code = "reserved system";
557 				break;
558 			case 1:
559 				code = "reserved device";
560 				break;
561 			case 2:
562 				code = "OEM defined";
563 				break;
564 			default:
565 				code = "reserved";
566 				break;
567 		}
568 		printf("APM: %s event code %x\n", code, event_code);
569 	}
570 }
571 
572 static void
573 apm_periodic_check(struct apm_softc *sc)
574 {
575 	int error;
576 	u_int event_code, event_info;
577 
578 
579 	/*
580 	 * tell the BIOS we're working on it, if asked to do a
581 	 * suspend/standby
582 	 */
583 	if (apm_op_inprog)
584 		sc->ops->set_powstate(sc->cookie, APM_DEV_ALLDEVS,
585 				      APM_LASTREQ_INPROG);
586 
587 	while ((error = sc->ops->get_event(sc->cookie, &event_code,
588 					   &event_info)) == 0
589 	       && !apm_damn_fool_bios)
590 		apm_event_handle(sc, event_code, event_info);
591 
592 	if (error != APM_ERR_NOEVENTS)
593 		apm_perror("get event", error);
594 	if (apm_suspends) {
595 		apm_op_inprog = 0;
596 		apm_suspend(sc);
597 	} else if (apm_standbys || apm_userstandbys) {
598 		apm_op_inprog = 0;
599 		apm_standby(sc);
600 	}
601 	apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
602 	apm_damn_fool_bios = 0;
603 }
604 
605 static void
606 apm_set_ver(struct apm_softc *self, u_long detail)
607 {
608 
609 	if (apm_v12_enabled &&
610 	    APM_MAJOR_VERS(detail) == 1 &&
611 	    APM_MINOR_VERS(detail) == 2) {
612 		apm_majver = 1;
613 		apm_minver = 2;
614 		goto ok;
615 	}
616 
617 	if (apm_v11_enabled &&
618 	    APM_MAJOR_VERS(detail) == 1 &&
619 	    APM_MINOR_VERS(detail) == 1) {
620 		apm_majver = 1;
621 		apm_minver = 1;
622 	} else {
623 		apm_majver = 1;
624 		apm_minver = 0;
625 	}
626 ok:
627 	printf("Power Management spec V%d.%d", apm_majver, apm_minver);
628 	apm_inited = 1;
629 	if (detail & APM_IDLE_SLOWS) {
630 #ifdef DIAGNOSTIC
631 		/* not relevant often */
632 		printf(" (slowidle)");
633 #endif
634 		/* leave apm_do_idle at its user-configured setting */
635 	} else
636 		apm_do_idle = 0;
637 #ifdef DIAGNOSTIC
638 	if (detail & APM_BIOS_PM_DISABLED)
639 		printf(" (BIOS mgmt disabled)");
640 	if (detail & APM_BIOS_PM_DISENGAGED)
641 		printf(" (BIOS managing devices)");
642 #endif
643 }
644 
645 static int
646 apmmatch(struct device *parent, struct cfdata *match, void *aux)
647 {
648 
649 	/* There can be only one! */
650 	if (apm_inited)
651 		return 0;
652 
653 	return (1);
654 }
655 
656 static void
657 apmattach(struct device *parent, struct device *self, void *aux)
658 {
659 	struct apm_softc *sc = (void *)self;
660 	struct apmdev_attach_args *aaa = aux;
661 	struct apm_power_info pinfo;
662 	u_int numbatts, capflags;
663 	int error;
664 
665 	printf(": ");
666 
667 	sc->ops = aaa->accessops;
668 	sc->cookie = aaa->accesscookie;
669 
670 	switch ((APM_MAJOR_VERS(aaa->apm_detail) << 8) +
671 		APM_MINOR_VERS(aaa->apm_detail)) {
672 	case 0x0100:
673 		apm_v11_enabled = 0;
674 		apm_v12_enabled = 0;
675 		break;
676 	case 0x0101:
677 		apm_v12_enabled = 0;
678 		/* fall through */
679 	case 0x0102:
680 	default:
681 		break;
682 	}
683 
684 	apm_set_ver(sc, aaa->apm_detail);	/* prints version info */
685 	printf("\n");
686 	if (apm_minver >= 2)
687 		sc->ops->get_capabilities(sc->cookie, &numbatts, &capflags);
688 
689 	/*
690 	 * enable power management
691 	 */
692 	sc->ops->enable(sc->cookie, 1);
693 
694 	error = sc->ops->get_powstat(sc->cookie, &pinfo);
695 	if (error == 0) {
696 #ifdef APM_POWER_PRINT
697 		apm_power_print(apmsc, &pinfo);
698 #endif
699 	} else
700 		apm_perror("get power status", error);
701 	sc->ops->cpu_busy(sc->cookie);
702 
703 	lockinit(&sc->sc_lock, PWAIT, "apmlk", 0, 0);
704 
705 	/* Initial state is `resumed'. */
706 	sc->sc_power_state = PWR_RESUME;
707 
708 	/* Do an initial check. */
709 	apm_periodic_check(sc);
710 
711 	/*
712 	 * Create a kernel thread to periodically check for APM events,
713 	 * and notify other subsystems when they occur.
714 	 */
715 	kthread_create(apm_create_thread, sc);
716 
717 	return;
718 }
719 
720 /*
721  * Print function (for parent devices).
722  */
723 int
724 apmprint(void *aux, const char *pnp)
725 {
726 	if (pnp)
727 		aprint_normal("apm at %s", pnp);
728 
729 	return (UNCONF);
730 }
731 
732 void
733 apm_create_thread(void *arg)
734 {
735 	struct apm_softc *sc = arg;
736 
737 	if (kthread_create1(apm_thread, sc, &sc->sc_thread,
738 			    "%s", sc->sc_dev.dv_xname) == 0)
739 		return;
740 
741 	/*
742 	 * We were unable to create the APM thread; bail out.
743 	 */
744 	sc->ops->disconnect(sc->cookie);
745 	printf("%s: unable to create thread, kernel APM support disabled\n",
746 	       sc->sc_dev.dv_xname);
747 }
748 
749 void
750 apm_thread(void *arg)
751 {
752 	struct apm_softc *apmsc = arg;
753 
754 	/*
755 	 * Loop forever, doing a periodic check for APM events.
756 	 */
757 	for (;;) {
758 		APM_LOCK(apmsc);
759 		apm_periodic_check(apmsc);
760 		APM_UNLOCK(apmsc);
761 		(void) tsleep(apmsc, PWAIT, "apmev",  (8 * hz) / 7);
762 	}
763 }
764 
765 int
766 apmdevopen(dev_t dev, int flag, int mode, struct proc *p)
767 {
768 	int unit = APMUNIT(dev);
769 	int ctl = APMDEV(dev);
770 	int error = 0;
771 	struct apm_softc *sc;
772 
773 	if (unit >= apmdev_cd.cd_ndevs)
774 		return ENXIO;
775 	sc = apmdev_cd.cd_devs[unit];
776 	if (!sc)
777 		return ENXIO;
778 
779 	if (!apm_inited)
780 		return ENXIO;
781 
782 	DPRINTF(APMDEBUG_DEVICE,
783 	    ("apmopen: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
784 
785 	APM_LOCK(sc);
786 	switch (ctl) {
787 	case APMDEV_CTL:
788 		if (!(flag & FWRITE)) {
789 			error = EINVAL;
790 			break;
791 		}
792 		if (sc->sc_flags & SCFLAG_OWRITE) {
793 			error = EBUSY;
794 			break;
795 		}
796 		sc->sc_flags |= SCFLAG_OWRITE;
797 		break;
798 	case APMDEV_NORMAL:
799 		if (!(flag & FREAD) || (flag & FWRITE)) {
800 			error = EINVAL;
801 			break;
802 		}
803 		sc->sc_flags |= SCFLAG_OREAD;
804 		break;
805 	default:
806 		error = ENXIO;
807 		break;
808 	}
809 	APM_UNLOCK(sc);
810 
811 	return (error);
812 }
813 
814 int
815 apmdevclose(dev_t dev, int flag, int mode, struct proc *p)
816 {
817 	struct apm_softc *sc = apmdev_cd.cd_devs[APMUNIT(dev)];
818 	int ctl = APMDEV(dev);
819 
820 	DPRINTF(APMDEBUG_DEVICE,
821 	    ("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
822 
823 	APM_LOCK(sc);
824 	switch (ctl) {
825 	case APMDEV_CTL:
826 		sc->sc_flags &= ~SCFLAG_OWRITE;
827 		break;
828 	case APMDEV_NORMAL:
829 		sc->sc_flags &= ~SCFLAG_OREAD;
830 		break;
831 	}
832 	if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
833 		sc->event_count = 0;
834 		sc->event_ptr = 0;
835 	}
836 	APM_UNLOCK(sc);
837 	return 0;
838 }
839 
840 int
841 apmdevioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
842 {
843 	struct apm_softc *sc = apmdev_cd.cd_devs[APMUNIT(dev)];
844 	struct apm_power_info *powerp;
845 	struct apm_event_info *evp;
846 #if 0
847 	struct apm_ctl *actl;
848 #endif
849 	int i, error = 0;
850 	int batt_flags;
851 
852 	APM_LOCK(sc);
853 	switch (cmd) {
854 	case APM_IOC_STANDBY:
855 		if (!apm_do_standby) {
856 			error = EOPNOTSUPP;
857 			break;
858 		}
859 
860 		if ((flag & FWRITE) == 0) {
861 			error = EBADF;
862 			break;
863 		}
864 		apm_userstandbys++;
865 		break;
866 
867 	case APM_IOC_SUSPEND:
868 		if ((flag & FWRITE) == 0) {
869 			error = EBADF;
870 			break;
871 		}
872 		apm_suspends++;
873 		break;
874 
875 	case APM_IOC_NEXTEVENT:
876 		if (!sc->event_count)
877 			error = EAGAIN;
878 		else {
879 			evp = (struct apm_event_info *)data;
880 			i = sc->event_ptr + APM_NEVENTS - sc->event_count;
881 			i %= APM_NEVENTS;
882 			*evp = sc->event_list[i];
883 			sc->event_count--;
884 		}
885 		break;
886 
887 	case APM_IOC_GETPOWER:
888 		powerp = (struct apm_power_info *)data;
889 		if ((error = sc->ops->get_powstat(sc->cookie, powerp)) != 0) {
890 			apm_perror("ioctl get power status", error);
891 			error = EIO;
892 			break;
893 		}
894 		switch (apm_minver) {
895 		case 0:
896 			break;
897 		case 1:
898 		default:
899 			batt_flags = powerp->battery_state;
900 			powerp->battery_state = APM_BATT_UNKNOWN;
901 			if (batt_flags & APM_BATT_FLAG_HIGH)
902 				powerp->battery_state = APM_BATT_HIGH;
903 			else if (batt_flags & APM_BATT_FLAG_LOW)
904 				powerp->battery_state = APM_BATT_LOW;
905 			else if (batt_flags & APM_BATT_FLAG_CRITICAL)
906 				powerp->battery_state = APM_BATT_CRITICAL;
907 			else if (batt_flags & APM_BATT_FLAG_CHARGING)
908 				powerp->battery_state = APM_BATT_CHARGING;
909 			else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
910 				powerp->battery_state = APM_BATT_ABSENT;
911 			break;
912 		}
913 		break;
914 
915 	default:
916 		error = ENOTTY;
917 	}
918 	APM_UNLOCK(sc);
919 
920 	return (error);
921 }
922 
923 int
924 apmdevpoll(dev_t dev, int events, struct proc *p)
925 {
926 	struct apm_softc *sc = apmdev_cd.cd_devs[APMUNIT(dev)];
927 	int revents = 0;
928 
929 	APM_LOCK(sc);
930 	if (events & (POLLIN | POLLRDNORM)) {
931 		if (sc->event_count)
932 			revents |= events & (POLLIN | POLLRDNORM);
933 		else
934 			selrecord(p, &sc->sc_rsel);
935 	}
936 	APM_UNLOCK(sc);
937 
938 	return (revents);
939 }
940 
941 static void
942 filt_apmrdetach(struct knote *kn)
943 {
944 	struct apm_softc *sc = kn->kn_hook;
945 
946 	APM_LOCK(sc);
947 	SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
948 	APM_UNLOCK(sc);
949 }
950 
951 static int
952 filt_apmread(struct knote *kn, long hint)
953 {
954 	struct apm_softc *sc = kn->kn_hook;
955 
956 	kn->kn_data = sc->event_count;
957 	return (kn->kn_data > 0);
958 }
959 
960 static const struct filterops apmread_filtops =
961 	{ 1, NULL, filt_apmrdetach, filt_apmread };
962 
963 int
964 apmdevkqfilter(dev_t dev, struct knote *kn)
965 {
966 	struct apm_softc *sc = apmdev_cd.cd_devs[APMUNIT(dev)];
967 	struct klist *klist;
968 
969 	switch (kn->kn_filter) {
970 	case EVFILT_READ:
971 		klist = &sc->sc_rsel.sel_klist;
972 		kn->kn_fop = &apmread_filtops;
973 		break;
974 
975 	default:
976 		return (1);
977 	}
978 
979 	kn->kn_hook = sc;
980 
981 	APM_LOCK(sc);
982 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
983 	APM_UNLOCK(sc);
984 
985 	return (0);
986 }
987