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