Exemplo n.º 1
0
void
apm_set_ver(struct apm_softc *self)
{
	struct apmregs regs;
	int rv = 0;

	bzero(&regs, sizeof(regs));
	regs.cx = APM_VERSION;

	if (APM_MAJOR(apm_flags) == 1 && APM_MINOR(apm_flags) == 2 &&
	    (rv = apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, &regs)) == 0) {
		apm_majver = APM_CONN_MAJOR(&regs);
		apm_minver = APM_CONN_MINOR(&regs);
	} else {
#ifdef APMDEBUG
		if (rv)
			apm_perror("set version 1.2", &regs);
#endif
		/* try downgrading to 1.1 */
		bzero(&regs, sizeof(regs));
		regs.cx = 0x0101;

		if (apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, &regs) == 0) {
			apm_majver = 1;
			apm_minver = 1;
		} else {
#ifdef APMDEBUG
			apm_perror("set version 1.1", &regs);
#endif
			/* stay w/ flags then */
			apm_majver = APM_MAJOR(apm_flags);
			apm_minver = APM_MINOR(apm_flags);

			/* fix version for some endianess-challenged compaqs */
			if (!apm_majver) {
				apm_majver = 1;
				apm_minver = 0;
			}
		}
	}
	printf(": Power Management spec V%d.%d", apm_majver, apm_minver);
#ifdef DIAGNOSTIC
	if (apm_flags & APM_IDLE_SLOWS)
		printf(" (slowidle)");
	if (apm_flags & APM_BIOS_PM_DISABLED)
		printf(" (BIOS management disabled)");
	if (apm_flags & APM_BIOS_PM_DISENGAGED)
		printf(" (BIOS managing devices)");
#endif
	printf("\n");
}
Exemplo n.º 2
0
static void
apm_periodic_check(struct apm_softc *sc)
{
	int error;
	u_int event_code, event_info;


	/*
	 * tell the BIOS we're working on it, if asked to do a
	 * suspend/standby
	 */
	if (apm_op_inprog)
		(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, APM_DEV_ALLDEVS,
		    APM_LASTREQ_INPROG);

	while ((error = (*sc->sc_ops->aa_get_event)(sc->sc_cookie, &event_code,
	    &event_info)) == 0 && !apm_damn_fool_bios)
		apm_event_handle(sc, event_code, event_info);

	if (error != APM_ERR_NOEVENTS)
		apm_perror("get event", error);
	if (apm_suspends) {
		apm_op_inprog = 0;
		apm_suspend(sc);
	} else if (apm_standbys || apm_userstandbys) {
		apm_op_inprog = 0;
		apm_standby(sc);
	}
	apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
	apm_damn_fool_bios = 0;
}
Exemplo n.º 3
0
void
apm_disconnect(struct apm_softc *sc)
{
	struct apmregs regs;

	bzero(&regs, sizeof(regs));
	if (apmcall(APM_SYSTEM_DEFAULTS,
	    (apm_minver == 1 ? APM_DEV_ALLDEVS : APM_DEFAULTS_ALL), &regs))
		apm_perror("system defaults failed", &regs);

	if (apmcall(APM_DISCONNECT, APM_DEV_APM_BIOS, &regs))
		apm_perror("disconnect failed", &regs);
	else
		printf("%s: disconnected\n", sc->sc_dev.dv_xname);
	apm_flags |= APM_BIOS_PM_DISABLED;
}
Exemplo n.º 4
0
void
apm_cpu_idle(void)
{
	struct apmregs regs;
	static u_int64_t call_apm_idle = 0;

	/*
	 * We call the bios APM_IDLE routine here only when we
	 * have been idle for some time - otherwise we just hlt.
	 */

	if  (call_apm_idle != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) {
		/* Always call BIOS halt/idle stuff */
		bzero(&regs, sizeof(regs));
		if (apmcall(APM_CPU_IDLE, 0, &regs) != 0) {
#ifdef DIAGNOSTIC
			apm_perror("set CPU idle", &regs);
#endif
		}

		/* If BIOS did not halt, halt now! */
		if (apm_flags & APM_IDLE_SLOWS) {
			__asm __volatile("sti;hlt");
		}
		call_apm_idle = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE];
	} else {
		__asm __volatile("sti;hlt");
	}
}
Exemplo n.º 5
0
void
apm_powmgt_enable(int onoff)
{
	struct apmregs regs;

	bzero(&regs, sizeof(regs));
	regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE;
	if (apmcall(APM_PWR_MGT_ENABLE,
	    (apm_minver? APM_DEV_APM_BIOS : APM_MGT_ALL), &regs) != 0)
		apm_perror("power management enable", &regs);
}
Exemplo n.º 6
0
int
apm_periodic_check(struct apm_softc *sc)
{
	struct apmregs regs;
	int ret = 0;

	if (apm_op_inprog)
		apm_set_powstate(APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);

	while (1) {
		if (apm_get_event(&regs) != 0) {
			/* i think some bioses combine the error codes */
			if (!(APM_ERR_CODE(&regs) & APM_ERR_NOEVENTS))
				apm_perror("get event", &regs);
			break;
		}

		/* If the APM BIOS tells us to suspend, don't do it twice */
		if (regs.bx == APM_SUSPEND_REQ)
			apm_lidclose = 0;
		if (apm_handle_event(sc, &regs))
			break;
	}

	if (apm_error || APM_ERR_CODE(&regs) == APM_ERR_NOTCONN)
		ret = -1;

	if (apm_lidclose) {
		apm_lidclose = 0;
		/* Fake a suspend request */
		regs.bx = APM_SUSPEND_REQ;
		apm_handle_event(sc, &regs);
	}
	if (apm_suspends /*|| (apm_battlow && apm_userstandbys)*/) {
		apm_op_inprog = 0;
		apm_suspend(APM_SYS_SUSPEND);
	} else if (apm_standbys || apm_userstandbys) {
		apm_op_inprog = 0;
		apm_suspend(APM_SYS_STANDBY);
	}
	apm_suspends = apm_standbys = apm_battlow = apm_userstandbys = 0;
	apm_error = 0;

	if (apm_resumes)
		apm_resumes--;
	return (ret);
}
Exemplo n.º 7
0
void
apm_cpu_busy(void)
{
	struct apmregs regs;

	if (!apm_slow_called)
		return;

	if (apm_flags & APM_IDLE_SLOWS) {
		bzero(&regs, sizeof(regs));
		if (apmcall(APM_CPU_BUSY, 0, &regs) != 0) {
#ifdef DIAGNOSTIC
			apm_perror("set CPU busy", &regs);
#endif
		}
		apm_slow_called = 0;
	}
}
Exemplo n.º 8
0
void
apm_cpu_slow(void)
{
	struct apmregs regs;
	static u_int64_t call_apm_slow = 0;

	if  (call_apm_slow != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) {
		/* Always call BIOS halt/idle stuff */
		bzero(&regs, sizeof(regs));
		if (apmcall(APM_CPU_IDLE, 0, &regs) != 0) {
#ifdef DIAGNOSTIC
			apm_perror("set CPU slow", &regs);
#endif
		}
		apm_slow_called = 1;
		call_apm_slow = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE];
	}
}
Exemplo n.º 9
0
int
apm_set_powstate(u_int dev, u_int state)
{
	struct apmregs regs;

	if (!apm_cd.cd_ndevs || (apm_minver == 0 && state > APM_SYS_OFF))
		return EINVAL;
	bzero(&regs, sizeof(regs));
	regs.cx = state;
	if (apmcall(APM_SET_PWR_STATE, dev, &regs) != 0) {
		apm_perror("set power state", &regs);
		if (APM_ERR_CODE(&regs) == APM_ERR_UNRECOG_DEV)
			return ENXIO;
		else
			return EIO;
	}
	return 0;
}
Exemplo n.º 10
0
int
apmioctl(dev_t dev, u_long cmd, void *data, int flag,
	struct lwp *l)
{
	struct apm_softc *sc = device_lookup_private(&apm_cd, APMUNIT(dev));
	struct apm_power_info *powerp;
	struct apm_event_info *evp;
#if 0
	struct apm_ctl *actl;
#endif
	int i, error = 0;
	int batt_flags;
	struct apm_ctl *actl;

	APM_LOCK(sc);
	switch (cmd) {
	case APM_IOC_STANDBY:
		if (!apm_do_standby) {
			error = EOPNOTSUPP;
			break;
		}

		if ((flag & FWRITE) == 0) {
			error = EBADF;
			break;
		}
		apm_userstandbys++;
		break;

	case APM_IOC_DEV_CTL:
		actl = (struct apm_ctl *)data;
		if ((flag & FWRITE) == 0) {
			error = EBADF;
			break;
		}
#if 0
		apm_get_powstate(actl->dev); /* XXX */
#endif
		error = (*sc->sc_ops->aa_set_powstate)(sc->sc_cookie, actl->dev,
		    actl->mode);
		apm_suspends++;
 		break;

	case APM_IOC_SUSPEND:
		if ((flag & FWRITE) == 0) {
			error = EBADF;
			break;
		}
		apm_suspends++;
		break;

	case APM_IOC_NEXTEVENT:
		if (!sc->sc_event_count)
			error = EAGAIN;
		else {
			evp = (struct apm_event_info *)data;
			i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count;
			i %= APM_NEVENTS;
			*evp = sc->sc_event_list[i];
			sc->sc_event_count--;
		}
		break;

	case OAPM_IOC_GETPOWER:
	case APM_IOC_GETPOWER:
		powerp = (struct apm_power_info *)data;
		if ((error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0,
		    powerp)) != 0) {
			apm_perror("ioctl get power status", error);
			error = EIO;
			break;
		}
		switch (apm_minver) {
		case 0:
			break;
		case 1:
		default:
			batt_flags = powerp->battery_flags;
			powerp->battery_state = APM_BATT_UNKNOWN;
			if (batt_flags & APM_BATT_FLAG_HIGH)
				powerp->battery_state = APM_BATT_HIGH;
			else if (batt_flags & APM_BATT_FLAG_LOW)
				powerp->battery_state = APM_BATT_LOW;
			else if (batt_flags & APM_BATT_FLAG_CRITICAL)
				powerp->battery_state = APM_BATT_CRITICAL;
			else if (batt_flags & APM_BATT_FLAG_CHARGING)
				powerp->battery_state = APM_BATT_CHARGING;
			else if (batt_flags & APM_BATT_FLAG_NO_SYSTEM_BATTERY)
				powerp->battery_state = APM_BATT_ABSENT;
			break;
		}
		break;

	default:
		error = ENOTTY;
	}
	APM_UNLOCK(sc);

	return (error);
}
Exemplo n.º 11
0
int
apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct apm_softc *sc;
	struct apmregs regs;
	int error = 0;

	/* apm0 only */
	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
		return ENXIO;

	rw_enter_write(&sc->sc_lock);
	switch (cmd) {
		/* some ioctl names from linux */
	case APM_IOC_STANDBY:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else
			apm_userstandbys++;
		break;
	case APM_IOC_SUSPEND:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else
			apm_suspends++;
		break;
	case APM_IOC_PRN_CTL:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else {
			int flag = *(int *)data;
			DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
			switch (flag) {
			case APM_PRINT_ON:	/* enable printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				break;
			case APM_PRINT_OFF: /* disable printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				sc->sc_flags |= SCFLAG_NOPRINT;
				break;
			case APM_PRINT_PCT: /* disable some printing */
				sc->sc_flags &= ~SCFLAG_PRINT;
				sc->sc_flags |= SCFLAG_PCTPRINT;
				break;
			default:
				error = EINVAL;
				break;
			}
		}
		break;
	case APM_IOC_DEV_CTL:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else {
			struct apm_ctl *actl = (struct apm_ctl *)data;

			bzero(&regs, sizeof(regs));
			if (!apmcall(APM_GET_POWER_STATE, actl->dev, &regs))
				printf("%s: dev %04x state %04x\n",
				    sc->sc_dev.dv_xname, dev, regs.cx);

			error = apm_set_powstate(actl->dev, actl->mode);
		}
		break;
	case APM_IOC_GETPOWER:
		if (apm_get_powstat(&regs) == 0) {
			struct apm_power_info *powerp =
			    (struct apm_power_info *)data;

			bzero(powerp, sizeof(*powerp));
			if (BATT_LIFE(&regs) != APM_BATT_LIFE_UNKNOWN)
				powerp->battery_life = BATT_LIFE(&regs);
			powerp->ac_state = AC_STATE(&regs);
			switch (apm_minver) {
			case 0:
				if (!(BATT_FLAGS(&regs) & APM_BATT_FLAG_NOBATTERY))
					powerp->battery_state = BATT_STATE(&regs);
				break;
			case 1:
			default:
				if (BATT_FLAGS(&regs) & APM_BATT_FLAG_HIGH)
					powerp->battery_state = APM_BATT_HIGH;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_LOW)
					powerp->battery_state = APM_BATT_LOW;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_CRITICAL)
					powerp->battery_state = APM_BATT_CRITICAL;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_CHARGING)
					powerp->battery_state = APM_BATT_CHARGING;
				else if (BATT_FLAGS(&regs) & APM_BATT_FLAG_NOBATTERY)
					powerp->battery_state = APM_BATTERY_ABSENT;
				else
					powerp->battery_state = APM_BATT_UNKNOWN;
				if (BATT_REM_VALID(&regs)) {
					powerp->minutes_left = BATT_REMAINING(&regs);
					if (sc->be_batt)
						powerp->minutes_left =
						    swap16(powerp->minutes_left);
				}
			}
		} else {
			apm_perror("ioctl get power status", &regs);
			error = EIO;
		}
		break;
	case APM_IOC_STANDBY_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		/* only fails if no one cares. apmd at least should */
		else if (apm_record_event(sc, APM_USER_STANDBY_REQ))
			error = EINVAL; /* ? */
		break;
	case APM_IOC_SUSPEND_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		/* only fails if no one cares. apmd at least should */
		else if (apm_record_event(sc, APM_USER_SUSPEND_REQ))
			error = EINVAL; /* ? */
		break;
	default:
		error = ENOTTY;
	}

	rw_exit_write(&sc->sc_lock);
	return error;
}
Exemplo n.º 12
0
void
apmattach(struct device *parent, struct device *self, void *aux)
{
	struct bios_attach_args *ba = aux;
	bios_apminfo_t *ap = ba->ba_apmp;
	struct apm_softc *sc = (void *)self;
	struct apmregs regs;
	u_int cbase, clen, l;
	bus_space_handle_t ch16, ch32, dh;

	apm_flags = ap->apm_detail;
	/*
	 * set up GDT descriptors for APM
	 */
	if (apm_flags & APM_32BIT_SUPPORTED) {

		/* truncate segments' limits to a page */
		ap->apm_code_len -= (ap->apm_code32_base +
		    ap->apm_code_len + 1) & 0xfff;
		ap->apm_code16_len -= (ap->apm_code16_base +
		    ap->apm_code16_len + 1) & 0xfff;
		ap->apm_data_len -= (ap->apm_data_base +
		    ap->apm_data_len + 1) & 0xfff;

		/* adjust version */
		if ((sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK) &&
		    (apm_flags & APM_VERMASK) !=
		    (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK))
			apm_flags = (apm_flags & ~APM_VERMASK) |
			    (sc->sc_dev.dv_cfdata->cf_flags & APM_VERMASK);
		if (sc->sc_dev.dv_cfdata->cf_flags & APM_NOCLI) {
			extern int apm_cli; /* from apmcall.S */
			apm_cli = 0;
		}
		if (sc->sc_dev.dv_cfdata->cf_flags & APM_BEBATT)
			sc->be_batt = 1;
		apm_ep.seg = GSEL(GAPM32CODE_SEL,SEL_KPL);
		apm_ep.entry = ap->apm_entry;
		cbase = min(ap->apm_code32_base, ap->apm_code16_base);
		clen = max(ap->apm_code32_base + ap->apm_code_len,
			   ap->apm_code16_base + ap->apm_code16_len) - cbase;
		if ((cbase <= ap->apm_data_base &&
		     cbase + clen >= ap->apm_data_base) ||
		    (ap->apm_data_base <= cbase &&
		     ap->apm_data_base + ap->apm_data_len >= cbase)) {
			l = max(ap->apm_data_base + ap->apm_data_len + 1,
				cbase + clen + 1) -
			    min(ap->apm_data_base, cbase);
			bus_space_map(ba->ba_memt,
				min(ap->apm_data_base, cbase),
				l, 1, &dh);
			ch16 = dh;
			if (ap->apm_data_base < cbase)
				ch16 += cbase - ap->apm_data_base;
			else
				dh += ap->apm_data_base - cbase;
		} else {

			bus_space_map(ba->ba_memt, cbase, clen + 1, 1, &ch16);
			bus_space_map(ba->ba_memt, ap->apm_data_base,
			    ap->apm_data_len + 1, 1, &dh);
		}
		ch32 = ch16;
		if (ap->apm_code16_base == cbase)
			ch32 += ap->apm_code32_base - cbase;
		else
			ch16 += ap->apm_code16_base - cbase;

		setgdt(GAPM32CODE_SEL, (void *)ch32, ap->apm_code_len,
		    SDT_MEMERA, SEL_KPL, 1, 0);
		setgdt(GAPM16CODE_SEL, (void *)ch16, ap->apm_code16_len,
		    SDT_MEMERA, SEL_KPL, 0, 0);
		setgdt(GAPMDATA_SEL, (void *)dh, ap->apm_data_len, SDT_MEMRWA,
		    SEL_KPL, 1, 0);
		DPRINTF((": flags %x code 32:%x/%x[%x] 16:%x/%x[%x] "
		    "data %x/%x/%x ep %x (%x:%x)\n%s", apm_flags,
		    ap->apm_code32_base, ch32, ap->apm_code_len,
		    ap->apm_code16_base, ch16, ap->apm_code16_len,
		    ap->apm_data_base, dh, ap->apm_data_len,
		    ap->apm_entry, apm_ep.seg, ap->apm_entry+ch32,
		    sc->sc_dev.dv_xname));

		apm_set_ver(sc);

		if (apm_flags & APM_BIOS_PM_DISABLED)
			apm_powmgt_enable(1);

		/* Engage cooperative power management on all devices (v1.1) */
		apm_powmgt_engage(1, APM_DEV_ALLDEVS);

		bzero(&regs, sizeof(regs));
		if (apm_get_powstat(&regs) != 0)
			apm_perror("get power status", &regs);
		apm_cpu_busy();

		rw_init(&sc->sc_lock, "apmlk");

		/*
		 * Do a check once, ignoring any errors. This avoids
		 * gratuitous APM disconnects on laptops where the first
		 * event in the queue (after a boot) is non-recognizable.
		 * The IBM ThinkPad 770Z is one of those.
		 */
		apm_periodic_check(sc);

		if (apm_periodic_check(sc) == -1) {
			apm_disconnect(sc);

			/* Failed, nuke APM idle loop */
			cpu_idle_enter_fcn = NULL;
			cpu_idle_cycle_fcn = NULL;
			cpu_idle_leave_fcn = NULL;
		} else {
			kthread_create_deferred(apm_thread_create, sc);

			/* Setup APM idle loop */
			if (apm_flags & APM_IDLE_SLOWS) {
				cpu_idle_enter_fcn = apm_cpu_slow;
				cpu_idle_cycle_fcn = NULL;
				cpu_idle_leave_fcn = apm_cpu_busy;
			} else {
				cpu_idle_enter_fcn = NULL;
				cpu_idle_cycle_fcn = apm_cpu_idle;
				cpu_idle_leave_fcn = NULL;
			}

			/* All is well, let the rest of the world know */
			acpiapm_open = apmopen;
			acpiapm_close = apmclose;
			acpiapm_ioctl = apmioctl;
			acpiapm_kqfilter = apmkqfilter;
			apm_attached = 1;
		}
	} else {
		setgdt(GAPM32CODE_SEL, NULL, 0, 0, 0, 0, 0);
		setgdt(GAPM16CODE_SEL, NULL, 0, 0, 0, 0, 0);
		setgdt(GAPMDATA_SEL, NULL, 0, 0, 0, 0, 0);
	}
}
Exemplo n.º 13
0
int
apm_handle_event(struct apm_softc *sc, struct apmregs *regs)
{
	struct apmregs nregs;
	int ret = 0;

	switch (regs->bx) {
	case APM_NOEVENT:
		ret++;
		break;

	case APM_USER_STANDBY_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("user wants STANDBY--fat chance\n"));
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("standby ourselves\n"));
			apm_userstandbys++;
		}
		break;
	case APM_STANDBY_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("standby requested\n"));
		if (apm_standbys || apm_suspends) {
			DPRINTF(("premature standby\n"));
			apm_error++;
			ret++;
		}
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("standby ourselves\n"));
			apm_standbys++;
		}
		break;
	case APM_USER_SUSPEND_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("user wants suspend--fat chance!\n"));
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("suspend ourselves\n"));
			apm_suspends++;
		}
		break;
	case APM_SUSPEND_REQ:
		if (apm_resumes || apm_op_inprog)
			break;
		DPRINTF(("suspend requested\n"));
		if (apm_standbys || apm_suspends) {
			DPRINTF(("premature suspend\n"));
			apm_error++;
			ret++;
		}
		apm_op_inprog++;
		if (apm_record_event(sc, regs->bx)) {
			DPRINTF(("suspend ourselves\n"));
			apm_suspends++;
		}
		break;
	case APM_POWER_CHANGE:
		DPRINTF(("power status change\n"));
		apm_get_powstat(&nregs);
		apm_record_event(sc, regs->bx);
		break;
	case APM_NORMAL_RESUME:
		DPRINTF(("system resumed\n"));
		apm_resume(sc, regs);
		break;
	case APM_CRIT_RESUME:
		DPRINTF(("system resumed without us!\n"));
		apm_resume(sc, regs);
		break;
	case APM_SYS_STANDBY_RESUME:
		DPRINTF(("system standby resume\n"));
		apm_resume(sc, regs);
		break;
	case APM_UPDATE_TIME:
		DPRINTF(("update time, please\n"));
		inittodr(time_second);
		apm_record_event(sc, regs->bx);
		break;
	case APM_CRIT_SUSPEND_REQ:
		DPRINTF(("suspend required immediately\n"));
		apm_record_event(sc, regs->bx);
		apm_suspend(APM_SYS_SUSPEND);
		break;
	case APM_BATTERY_LOW:
		DPRINTF(("Battery low!\n"));
		apm_battlow++;
		apm_record_event(sc, regs->bx);
		break;
	case APM_CAPABILITY_CHANGE:
		DPRINTF(("capability change\n"));
		if (apm_minver < 2) {
			DPRINTF(("adult event\n"));
		} else {
			if (apmcall(APM_GET_CAPABILITIES, APM_DEV_APM_BIOS,
			    &nregs) != 0) {
				apm_perror("get capabilities", &nregs);
			} else {
				apm_get_powstat(&nregs);
			}
		}
		break;
	default: {
#ifdef APMDEBUG
		char *p;
		switch (regs->bx >> 8) {
		case 0:	p = "reserved system";	break;
		case 1:	p = "reserved device";	break;
		case 2:	p = "OEM defined";	break;
		default:p = "reserved";		break;
		}
#endif
		DPRINTF(("apm_handle_event: %s event, code %d\n", p, regs->bx));
	    }
	}
	return ret;
}