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