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