xref: /netbsd-src/sys/dev/apm/apm.c (revision ce2c90c7c172d95d2402a5b3d96d8f8e6d138a21)
1 /*	$NetBSD: apm.c,v 1.7 2006/10/12 06:56:48 xtraeme 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: apm.c,v 1.7 2006/10/12 06:56:48 xtraeme Exp $");
44 
45 #include "opt_apm.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/apm/apmvar.h>
72 
73 #include <machine/stdarg.h>
74 
75 #ifdef APMDEBUG
76 #define DPRINTF(f, x)		do { if (apmdebug & (f)) printf x; } while (0)
77 
78 
79 #ifdef APMDEBUG_VALUE
80 int	apmdebug = APMDEBUG_VALUE;
81 #else
82 int	apmdebug = 0;
83 #endif /* APMDEBUG_VALUE */
84 
85 #else
86 #define	DPRINTF(f, x)		/**/
87 #endif /* APMDEBUG */
88 
89 #define	SCFLAG_OREAD	0x0000001
90 #define	SCFLAG_OWRITE	0x0000002
91 #define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)
92 
93 #define	APMUNIT(dev)	(minor(dev)&0xf0)
94 #define	APM(dev)	(minor(dev)&0x0f)
95 #define APM_NORMAL	0
96 #define APM_CTL	8
97 
98 /*
99  * A brief note on the locking protocol: it's very simple; we
100  * assert an exclusive lock any time thread context enters the
101  * APM module.  This is both the APM thread itself, as well as
102  * user context.
103  */
104 #define	APM_LOCK(apmsc)						\
105 	(void) lockmgr(&(apmsc)->sc_lock, LK_EXCLUSIVE, NULL)
106 #define	APM_UNLOCK(apmsc)						\
107 	(void) lockmgr(&(apmsc)->sc_lock, LK_RELEASE, NULL)
108 
109 static void	apm_event_handle(struct apm_softc *, u_int, u_int);
110 static void	apm_periodic_check(struct apm_softc *);
111 static void	apm_create_thread(void *);
112 static void	apm_thread(void *);
113 static void	apm_perror(const char *, int, ...)
114 		    __attribute__((__format__(__printf__,1,3)));
115 #ifdef APM_POWER_PRINT
116 static void	apm_power_print(struct apm_softc *, struct apm_power_info *);
117 #endif
118 static int	apm_record_event(struct apm_softc *, u_int);
119 static void	apm_set_ver(struct apm_softc *);
120 static void	apm_standby(struct apm_softc *);
121 static void	apm_suspend(struct apm_softc *);
122 static void	apm_resume(struct apm_softc *, u_int, u_int);
123 
124 extern struct cfdriver apm_cd;
125 
126 dev_type_open(apmopen);
127 dev_type_close(apmclose);
128 dev_type_ioctl(apmioctl);
129 dev_type_poll(apmpoll);
130 dev_type_kqfilter(apmkqfilter);
131 
132 const struct cdevsw apm_cdevsw = {
133 	apmopen, apmclose, noread, nowrite, apmioctl,
134 	nostop, notty, apmpoll, nommap, apmkqfilter, D_OTHER,
135 };
136 
137 /* configurable variables */
138 int	apm_bogus_bios = 0;
139 #ifdef APM_DISABLE
140 int	apm_enabled = 0;
141 #else
142 int	apm_enabled = 1;
143 #endif
144 #ifdef APM_NO_IDLE
145 int	apm_do_idle = 0;
146 #else
147 int	apm_do_idle = 1;
148 #endif
149 #ifdef APM_NO_STANDBY
150 int	apm_do_standby = 0;
151 #else
152 int	apm_do_standby = 1;
153 #endif
154 #ifdef APM_V10_ONLY
155 int	apm_v11_enabled = 0;
156 #else
157 int	apm_v11_enabled = 1;
158 #endif
159 #ifdef APM_NO_V12
160 int	apm_v12_enabled = 0;
161 #else
162 int	apm_v12_enabled = 1;
163 #endif
164 #ifdef APM_FORCE_64K_SEGMENTS
165 int	apm_force_64k_segments = 1;
166 #else
167 int	apm_force_64k_segments = 0;
168 #endif
169 #ifdef APM_ALLOW_BOGUS_SEGMENTS
170 int	apm_allow_bogus_segments = 1;
171 #else
172 int	apm_allow_bogus_segments = 0;
173 #endif
174 
175 /* variables used during operation (XXX cgd) */
176 u_char	apm_majver, apm_minver;
177 int	apm_inited;
178 int	apm_standbys, apm_userstandbys, apm_suspends, apm_battlow;
179 int	apm_damn_fool_bios, apm_op_inprog;
180 int	apm_evindex;
181 
182 static int apm_spl;		/* saved spl while suspended */
183 
184 const char *
185 apm_strerror(int code)
186 {
187 	switch (code) {
188 	case APM_ERR_PM_DISABLED:
189 		return ("power management disabled");
190 	case APM_ERR_REALALREADY:
191 		return ("real mode interface already connected");
192 	case APM_ERR_NOTCONN:
193 		return ("interface not connected");
194 	case APM_ERR_16ALREADY:
195 		return ("16-bit interface already connected");
196 	case APM_ERR_16NOTSUPP:
197 		return ("16-bit interface not supported");
198 	case APM_ERR_32ALREADY:
199 		return ("32-bit interface already connected");
200 	case APM_ERR_32NOTSUPP:
201 		return ("32-bit interface not supported");
202 	case APM_ERR_UNRECOG_DEV:
203 		return ("unrecognized device ID");
204 	case APM_ERR_ERANGE:
205 		return ("parameter out of range");
206 	case APM_ERR_NOTENGAGED:
207 		return ("interface not engaged");
208 	case APM_ERR_UNABLE:
209 		return ("unable to enter requested state");
210 	case APM_ERR_NOEVENTS:
211 		return ("no pending events");
212 	case APM_ERR_NOT_PRESENT:
213 		return ("no APM present");
214 	default:
215 		return ("unknown error code");
216 	}
217 }
218 
219 static void
220 apm_perror(const char *str, int errinfo, ...) /* XXX cgd */
221 {
222 	va_list ap;
223 
224 	printf("APM ");
225 
226 	va_start(ap, errinfo);
227 	vprintf(str, ap);			/* XXX cgd */
228 	va_end(ap);
229 
230 	printf(": %s\n", apm_strerror(errinfo));
231 }
232 
233 #ifdef APM_POWER_PRINT
234 static void
235 apm_power_print(struct apm_softc *sc, struct apm_power_info *pi)
236 {
237 
238 	if (pi->battery_life != APM_BATT_LIFE_UNKNOWN) {
239 		printf("%s: battery life expectancy: %d%%\n",
240 		    sc->sc_dev.dv_xname, pi->battery_life);
241 	}
242 	printf("%s: A/C state: ", sc->sc_dev.dv_xname);
243 	switch (pi->ac_state) {
244 	case APM_AC_OFF:
245 		printf("off\n");
246 		break;
247 	case APM_AC_ON:
248 		printf("on\n");
249 		break;
250 	case APM_AC_BACKUP:
251 		printf("backup power\n");
252 		break;
253 	default:
254 	case APM_AC_UNKNOWN:
255 		printf("unknown\n");
256 		break;
257 	}
258 	printf("%s: battery charge state:", sc->sc_dev.dv_xname);
259 	if (apm_minver == 0)
260 		switch (pi->battery_state) {
261 		case APM_BATT_HIGH:
262 			printf("high\n");
263 			break;
264 		case APM_BATT_LOW:
265 			printf("low\n");
266 			break;
267 		case APM_BATT_CRITICAL:
268 			printf("critical\n");
269 			break;
270 		case APM_BATT_CHARGING:
271 			printf("charging\n");
272 			break;
273 		case APM_BATT_UNKNOWN:
274 			printf("unknown\n");
275 			break;
276 		default:
277 			printf("undecoded state %x\n", pi->battery_state);
278 			break;
279 		}
280 	else if (apm_minver >= 1) {
281 		if (pi->battery_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
282 			printf(" no battery");
283 		else {
284 			if (pi->battery_flags & APM_BATT_FLAG_HIGH)
285 				printf(" high");
286 			if (pi->battery_flags & APM_BATT_FLAG_LOW)
287 				printf(" low");
288 			if (pi->battery_flags & APM_BATT_FLAG_CRITICAL)
289 				printf(" critical");
290 			if (pi->battery_flags & APM_BATT_FLAG_CHARGING)
291 				printf(" charging");
292 		}
293 		printf("\n");
294 		if (pi->minutes_valid) {
295 			printf("%s: estimated ", sc->sc_dev.dv_xname);
296 			if (pi->minutes_left / 60)
297 				printf("%dh ", pi->minutes_left / 60);
298 			printf("%dm\n", pi->minutes_left % 60);
299 		}
300 	}
301 	return;
302 }
303 #endif
304 
305 static void
306 apm_suspend(struct apm_softc *sc)
307 {
308 	int error;
309 
310 	if (sc->sc_power_state == PWR_SUSPEND) {
311 #ifdef APMDEBUG
312 		printf("%s: apm_suspend: already suspended?\n",
313 		    sc->sc_dev.dv_xname);
314 #endif
315 		return;
316 	}
317 	sc->sc_power_state = PWR_SUSPEND;
318 
319 	if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) {
320 		dopowerhooks(PWR_SOFTSUSPEND);
321 
322 		apm_spl = splhigh();
323 
324 		dopowerhooks(PWR_SUSPEND);
325 	}
326 
327 	error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
328 	    APM_SYS_SUSPEND);
329 
330 	if (error)
331 		apm_resume(sc, 0, 0);
332 }
333 
334 static void
335 apm_standby(struct apm_softc *sc)
336 {
337 	int error;
338 
339 	if (sc->sc_power_state == PWR_STANDBY) {
340 #ifdef APMDEBUG
341 		printf("%s: apm_standby: already standing by?\n",
342 		    sc->sc_dev.dv_xname);
343 #endif
344 		return;
345 	}
346 	sc->sc_power_state = PWR_STANDBY;
347 
348 	if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) {
349 		dopowerhooks(PWR_SOFTSTANDBY);
350 
351 		apm_spl = splhigh();
352 
353 		dopowerhooks(PWR_STANDBY);
354 	}
355 	error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
356 	    APM_SYS_STANDBY);
357 	if (error)
358 		apm_resume(sc, 0, 0);
359 }
360 
361 static void
362 apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info __unused)
363 {
364 
365 	if (sc->sc_power_state == PWR_RESUME) {
366 #ifdef APMDEBUG
367 		printf("%s: apm_resume: already running?\n",
368 		    sc->sc_dev.dv_xname);
369 #endif
370 		return;
371 	}
372 	sc->sc_power_state = PWR_RESUME;
373 
374 #ifdef TIMER_FREQ
375 	/*
376 	 * Some system requires its clock to be initialized after hybernation.
377 	 */
378 	initrtclock(TIMER_FREQ);
379 #endif
380 
381 	inittodr(time_second);
382 	if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) {
383 		dopowerhooks(PWR_RESUME);
384 
385 		splx(apm_spl);
386 
387 		dopowerhooks(PWR_SOFTRESUME);
388 	}
389 
390 	apm_record_event(sc, event_type);
391 }
392 
393 /*
394  * return 0 if the user will notice and handle the event,
395  * return 1 if the kernel driver should do so.
396  */
397 static int
398 apm_record_event(struct apm_softc *sc, u_int event_type)
399 {
400 	struct apm_event_info *evp;
401 
402 	if ((sc->sc_flags & SCFLAG_OPEN) == 0)
403 		return 1;		/* no user waiting */
404 	if (sc->sc_event_count == APM_NEVENTS)
405 		return 1;			/* overflow */
406 	evp = &sc->sc_event_list[sc->sc_event_ptr];
407 	sc->sc_event_count++;
408 	sc->sc_event_ptr++;
409 	sc->sc_event_ptr %= APM_NEVENTS;
410 	evp->type = event_type;
411 	evp->index = ++apm_evindex;
412 	selnotify(&sc->sc_rsel, 0);
413 	return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */
414 }
415 
416 static void
417 apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info)
418 {
419 	int error;
420 	const char *code;
421 	struct apm_power_info pi;
422 
423 	switch (event_code) {
424 	case APM_USER_STANDBY_REQ:
425 		DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n"));
426 		if (apm_do_standby) {
427 			if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
428 				apm_userstandbys++;
429 			apm_op_inprog++;
430 			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
431 			    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
432 		} else {
433 			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
434 			    APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
435 			/* in case BIOS hates being spurned */
436 			(*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
437 		}
438 		break;
439 
440 	case APM_STANDBY_REQ:
441 		DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n"));
442 		if (apm_standbys || apm_suspends) {
443 			DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
444 			    ("damn fool BIOS did not wait for answer\n"));
445 			/* just give up the fight */
446 			apm_damn_fool_bios = 1;
447 		}
448 		if (apm_do_standby) {
449 			if (apm_op_inprog == 0 &&
450 			    apm_record_event(sc, event_code))
451 				apm_standbys++;
452 			apm_op_inprog++;
453 			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
454 			    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
455 		} else {
456 			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
457 			    APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
458 			/* in case BIOS hates being spurned */
459 			(*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
460 		}
461 		break;
462 
463 	case APM_USER_SUSPEND_REQ:
464 		DPRINTF(APMDEBUG_EVENTS, ("apmev: user suspend request\n"));
465 		if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
466 			apm_suspends++;
467 		apm_op_inprog++;
468 		(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
469 		    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
470 		break;
471 
472 	case APM_SUSPEND_REQ:
473 		DPRINTF(APMDEBUG_EVENTS, ("apmev: system suspend request\n"));
474 		if (apm_standbys || apm_suspends) {
475 			DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
476 			    ("damn fool BIOS did not wait for answer\n"));
477 			/* just give up the fight */
478 			apm_damn_fool_bios = 1;
479 		}
480 		if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
481 			apm_suspends++;
482 		apm_op_inprog++;
483 		(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
484 		    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
485 		break;
486 
487 	case APM_POWER_CHANGE:
488 		DPRINTF(APMDEBUG_EVENTS, ("apmev: power status change\n"));
489 		error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
490 #ifdef APM_POWER_PRINT
491 		/* only print if nobody is catching events. */
492 		if (error == 0 &&
493 		    (sc->sc_flags & (SCFLAG_OREAD|SCFLAG_OWRITE)) == 0)
494 			apm_power_print(sc, &pi);
495 #endif
496 		apm_record_event(sc, event_code);
497 		break;
498 
499 	case APM_NORMAL_RESUME:
500 		DPRINTF(APMDEBUG_EVENTS, ("apmev: resume system\n"));
501 		apm_resume(sc, event_code, event_info);
502 		break;
503 
504 	case APM_CRIT_RESUME:
505 		DPRINTF(APMDEBUG_EVENTS, ("apmev: critical resume system"));
506 		apm_resume(sc, event_code, event_info);
507 		break;
508 
509 	case APM_SYS_STANDBY_RESUME:
510 		DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby resume\n"));
511 		apm_resume(sc, event_code, event_info);
512 		break;
513 
514 	case APM_UPDATE_TIME:
515 		DPRINTF(APMDEBUG_EVENTS, ("apmev: update time\n"));
516 		apm_resume(sc, event_code, event_info);
517 		break;
518 
519 	case APM_CRIT_SUSPEND_REQ:
520 		DPRINTF(APMDEBUG_EVENTS, ("apmev: critical system suspend\n"));
521 		apm_record_event(sc, event_code);
522 		apm_suspend(sc);
523 		break;
524 
525 	case APM_BATTERY_LOW:
526 		DPRINTF(APMDEBUG_EVENTS, ("apmev: battery low\n"));
527 		apm_battlow++;
528 		apm_record_event(sc, event_code);
529 		break;
530 
531 	case APM_CAP_CHANGE:
532 		DPRINTF(APMDEBUG_EVENTS, ("apmev: capability change\n"));
533 		if (apm_minver < 2) {
534 			DPRINTF(APMDEBUG_EVENTS, ("apm: unexpected event\n"));
535 		} else {
536 			u_int numbatts, capflags;
537 			(*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie,
538 			    &numbatts, &capflags);
539 			(*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
540 		}
541 		break;
542 
543 	default:
544 		switch (event_code >> 8) {
545 			case 0:
546 				code = "reserved system";
547 				break;
548 			case 1:
549 				code = "reserved device";
550 				break;
551 			case 2:
552 				code = "OEM defined";
553 				break;
554 			default:
555 				code = "reserved";
556 				break;
557 		}
558 		printf("APM: %s event code %x\n", code, event_code);
559 	}
560 }
561 
562 static void
563 apm_periodic_check(struct apm_softc *sc)
564 {
565 	int error;
566 	u_int event_code, event_info;
567 
568 
569 	/*
570 	 * tell the BIOS we're working on it, if asked to do a
571 	 * suspend/standby
572 	 */
573 	if (apm_op_inprog)
574 		(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
575 		    APM_LASTREQ_INPROG);
576 
577 	while ((error = (*sc->sc_ops->aa_get_event)(sc->sc_cookie, &event_code,
578 	    &event_info)) == 0 && !apm_damn_fool_bios)
579 		apm_event_handle(sc, event_code, event_info);
580 
581 	if (error != APM_ERR_NOEVENTS)
582 		apm_perror("get event", error);
583 	if (apm_suspends) {
584 		apm_op_inprog = 0;
585 		apm_suspend(sc);
586 	} else if (apm_standbys || apm_userstandbys) {
587 		apm_op_inprog = 0;
588 		apm_standby(sc);
589 	}
590 	apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
591 	apm_damn_fool_bios = 0;
592 }
593 
594 static void
595 apm_set_ver(struct apm_softc *sc)
596 {
597 
598 	if (apm_v12_enabled &&
599 	    APM_MAJOR_VERS(sc->sc_vers) == 1 &&
600 	    APM_MINOR_VERS(sc->sc_vers) == 2) {
601 		apm_majver = 1;
602 		apm_minver = 2;
603 		goto ok;
604 	}
605 
606 	if (apm_v11_enabled &&
607 	    APM_MAJOR_VERS(sc->sc_vers) == 1 &&
608 	    APM_MINOR_VERS(sc->sc_vers) == 1) {
609 		apm_majver = 1;
610 		apm_minver = 1;
611 	} else {
612 		apm_majver = 1;
613 		apm_minver = 0;
614 	}
615 ok:
616 	printf("Power Management spec V%d.%d", apm_majver, apm_minver);
617 	apm_inited = 1;
618 	if (sc->sc_detail & APM_IDLE_SLOWS) {
619 #ifdef DIAGNOSTIC
620 		/* not relevant often */
621 		printf(" (slowidle)");
622 #endif
623 		/* leave apm_do_idle at its user-configured setting */
624 	} else
625 		apm_do_idle = 0;
626 #ifdef DIAGNOSTIC
627 	if (sc->sc_detail & APM_BIOS_PM_DISABLED)
628 		printf(" (BIOS mgmt disabled)");
629 	if (sc->sc_detail & APM_BIOS_PM_DISENGAGED)
630 		printf(" (BIOS managing devices)");
631 #endif
632 }
633 
634 int
635 apm_match(void)
636 {
637 	static int got;
638 	return !got++;
639 }
640 
641 void
642 apm_attach(struct apm_softc *sc)
643 {
644 	struct apm_power_info pinfo;
645 	u_int numbatts, capflags;
646 	int error;
647 
648 	printf(": ");
649 
650 	switch ((APM_MAJOR_VERS(sc->sc_vers) << 8) + APM_MINOR_VERS(sc->sc_vers)) {
651 	case 0x0100:
652 		apm_v11_enabled = 0;
653 		apm_v12_enabled = 0;
654 		break;
655 	case 0x0101:
656 		apm_v12_enabled = 0;
657 		/* fall through */
658 	case 0x0102:
659 	default:
660 		break;
661 	}
662 
663 	apm_set_ver(sc);	/* prints version info */
664 	printf("\n");
665 	if (apm_minver >= 2)
666 		(*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie, &numbatts,
667 		    &capflags);
668 
669 	/*
670 	 * enable power management
671 	 */
672 	(*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
673 
674 	error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pinfo);
675 	if (error == 0) {
676 #ifdef APM_POWER_PRINT
677 		apm_power_print(sc, &pinfo);
678 #endif
679 	} else
680 		apm_perror("get power status", error);
681 	if (sc->sc_ops->aa_cpu_busy)
682 		(*sc->sc_ops->aa_cpu_busy)(sc->sc_cookie);
683 
684 	lockinit(&sc->sc_lock, PWAIT, "apmlk", 0, 0);
685 
686 	/* Initial state is `resumed'. */
687 	sc->sc_power_state = PWR_RESUME;
688 
689 	/* Do an initial check. */
690 	apm_periodic_check(sc);
691 
692 	/*
693 	 * Create a kernel thread to periodically check for APM events,
694 	 * and notify other subsystems when they occur.
695 	 */
696 	kthread_create(apm_create_thread, sc);
697 
698 	return;
699 }
700 
701 void
702 apm_create_thread(void *arg)
703 {
704 	struct apm_softc *sc = arg;
705 
706 	if (kthread_create1(apm_thread, sc, &sc->sc_thread,
707 			    "%s", sc->sc_dev.dv_xname) == 0)
708 		return;
709 
710 	/*
711 	 * We were unable to create the APM thread; bail out.
712 	 */
713 	if (sc->sc_ops->aa_disconnect)
714 		(*sc->sc_ops->aa_disconnect)(sc->sc_cookie);
715 	printf("%s: unable to create thread, kernel APM support disabled\n",
716 	       sc->sc_dev.dv_xname);
717 }
718 
719 void
720 apm_thread(void *arg)
721 {
722 	struct apm_softc *apmsc = arg;
723 
724 	/*
725 	 * Loop forever, doing a periodic check for APM events.
726 	 */
727 	for (;;) {
728 		APM_LOCK(apmsc);
729 		apm_periodic_check(apmsc);
730 		APM_UNLOCK(apmsc);
731 		(void) tsleep(apmsc, PWAIT, "apmev",  (8 * hz) / 7);
732 	}
733 }
734 
735 int
736 apmopen(dev_t dev, int flag, int mode __unused, struct lwp *l __unused)
737 {
738 	int unit = APMUNIT(dev);
739 	int ctl = APM(dev);
740 	int error = 0;
741 	struct apm_softc *sc;
742 
743 	if (unit >= apm_cd.cd_ndevs)
744 		return ENXIO;
745 	sc = apm_cd.cd_devs[unit];
746 	if (!sc)
747 		return ENXIO;
748 
749 	if (!apm_inited)
750 		return ENXIO;
751 
752 	DPRINTF(APMDEBUG_DEVICE,
753 	    ("apmopen: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
754 
755 	APM_LOCK(sc);
756 	switch (ctl) {
757 	case APM_CTL:
758 		if (!(flag & FWRITE)) {
759 			error = EINVAL;
760 			break;
761 		}
762 		if (sc->sc_flags & SCFLAG_OWRITE) {
763 			error = EBUSY;
764 			break;
765 		}
766 		sc->sc_flags |= SCFLAG_OWRITE;
767 		break;
768 	case APM_NORMAL:
769 		if (!(flag & FREAD) || (flag & FWRITE)) {
770 			error = EINVAL;
771 			break;
772 		}
773 		sc->sc_flags |= SCFLAG_OREAD;
774 		break;
775 	default:
776 		error = ENXIO;
777 		break;
778 	}
779 	APM_UNLOCK(sc);
780 
781 	return (error);
782 }
783 
784 int
785 apmclose(dev_t dev, int flag __unused, int mode __unused,
786 	struct lwp *l __unused)
787 {
788 	struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
789 	int ctl = APM(dev);
790 
791 	DPRINTF(APMDEBUG_DEVICE,
792 	    ("apmclose: pid %d flag %x mode %x\n", l->l_proc->p_pid, flag, mode));
793 
794 	APM_LOCK(sc);
795 	switch (ctl) {
796 	case APM_CTL:
797 		sc->sc_flags &= ~SCFLAG_OWRITE;
798 		break;
799 	case APM_NORMAL:
800 		sc->sc_flags &= ~SCFLAG_OREAD;
801 		break;
802 	}
803 	if ((sc->sc_flags & SCFLAG_OPEN) == 0) {
804 		sc->sc_event_count = 0;
805 		sc->sc_event_ptr = 0;
806 	}
807 	APM_UNLOCK(sc);
808 	return 0;
809 }
810 
811 int
812 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag,
813 	struct lwp *l __unused)
814 {
815 	struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
816 	struct apm_power_info *powerp;
817 	struct apm_event_info *evp;
818 #if 0
819 	struct apm_ctl *actl;
820 #endif
821 	int i, error = 0;
822 	int batt_flags;
823 	struct apm_ctl *actl;
824 
825 	APM_LOCK(sc);
826 	switch (cmd) {
827 	case APM_IOC_STANDBY:
828 		if (!apm_do_standby) {
829 			error = EOPNOTSUPP;
830 			break;
831 		}
832 
833 		if ((flag & FWRITE) == 0) {
834 			error = EBADF;
835 			break;
836 		}
837 		apm_userstandbys++;
838 		break;
839 
840 	case APM_IOC_DEV_CTL:
841 		actl = (struct apm_ctl *)data;
842 		if ((flag & FWRITE) == 0) {
843 			error = EBADF;
844 			break;
845 		}
846 #if 0
847 		apm_get_powstate(actl->dev); /* XXX */
848 #endif
849 		error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, actl->dev,
850 		    actl->mode);
851 		apm_suspends++;
852  		break;
853 
854 	case APM_IOC_SUSPEND:
855 		if ((flag & FWRITE) == 0) {
856 			error = EBADF;
857 			break;
858 		}
859 		apm_suspends++;
860 		break;
861 
862 	case APM_IOC_NEXTEVENT:
863 		if (!sc->sc_event_count)
864 			error = EAGAIN;
865 		else {
866 			evp = (struct apm_event_info *)data;
867 			i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
868 			i %= APM_NEVENTS;
869 			*evp = sc->sc_event_list[i];
870 			sc->sc_event_count--;
871 		}
872 		break;
873 
874 	case APM_IOC_GETPOWER:
875 		powerp = (struct apm_power_info *)data;
876 		if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0,
877 		    powerp)) != 0) {
878 			apm_perror("ioctl get power status", error);
879 			error = EIO;
880 			break;
881 		}
882 		switch (apm_minver) {
883 		case 0:
884 			break;
885 		case 1:
886 		default:
887 			batt_flags = powerp->battery_flags;
888 			powerp->battery_state = APM_BATT_UNKNOWN;
889 			if (batt_flags & APM_BATT_FLAG_HIGH)
890 				powerp->battery_state = APM_BATT_HIGH;
891 			else if (batt_flags & APM_BATT_FLAG_LOW)
892 				powerp->battery_state = APM_BATT_LOW;
893 			else if (batt_flags & APM_BATT_FLAG_CRITICAL)
894 				powerp->battery_state = APM_BATT_CRITICAL;
895 			else if (batt_flags & APM_BATT_FLAG_CHARGING)
896 				powerp->battery_state = APM_BATT_CHARGING;
897 			else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
898 				powerp->battery_state = APM_BATT_ABSENT;
899 			break;
900 		}
901 		break;
902 
903 	default:
904 		error = ENOTTY;
905 	}
906 	APM_UNLOCK(sc);
907 
908 	return (error);
909 }
910 
911 int
912 apmpoll(dev_t dev, int events, struct lwp *l)
913 {
914 	struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
915 	int revents = 0;
916 
917 	APM_LOCK(sc);
918 	if (events & (POLLIN | POLLRDNORM)) {
919 		if (sc->sc_event_count)
920 			revents |= events & (POLLIN | POLLRDNORM);
921 		else
922 			selrecord(l, &sc->sc_rsel);
923 	}
924 	APM_UNLOCK(sc);
925 
926 	return (revents);
927 }
928 
929 static void
930 filt_apmrdetach(struct knote *kn)
931 {
932 	struct apm_softc *sc = kn->kn_hook;
933 
934 	APM_LOCK(sc);
935 	SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext);
936 	APM_UNLOCK(sc);
937 }
938 
939 static int
940 filt_apmread(struct knote *kn, long hint __unused)
941 {
942 	struct apm_softc *sc = kn->kn_hook;
943 
944 	kn->kn_data = sc->sc_event_count;
945 	return (kn->kn_data > 0);
946 }
947 
948 static const struct filterops apmread_filtops =
949 	{ 1, NULL, filt_apmrdetach, filt_apmread };
950 
951 int
952 apmkqfilter(dev_t dev, struct knote *kn)
953 {
954 	struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)];
955 	struct klist *klist;
956 
957 	switch (kn->kn_filter) {
958 	case EVFILT_READ:
959 		klist = &sc->sc_rsel.sel_klist;
960 		kn->kn_fop = &apmread_filtops;
961 		break;
962 
963 	default:
964 		return (1);
965 	}
966 
967 	kn->kn_hook = sc;
968 
969 	APM_LOCK(sc);
970 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
971 	APM_UNLOCK(sc);
972 
973 	return (0);
974 }
975