Exemplo n.º 1
0
int
apm_handle_event(struct pxa2x0_apm_softc *sc, u_int type)
{
	struct	apm_power_info power;
	int	ret = 0;

	switch (type) {
	case APM_NOEVENT:
		ret = 1;
		break;
	case APM_CRIT_SUSPEND_REQ:
		DPRINTF(("suspend required immediately\n"));
#if 0
		/* XXX apmd would make us suspend again after resume. */
		(void)apm_record_event(sc, type);
#endif
		/*
		 * We ignore APM_CRIT_RESUME and just suspend here as usual
		 * to simplify the actual apm_get_event() implementation.
		 */
		apm_suspends++;
		ret = 1;
		break;
	case APM_USER_SUSPEND_REQ:
	case APM_SUSPEND_REQ:
		DPRINTF(("suspend requested\n"));
		if (apm_record_event(sc, type)) {
			DPRINTF(("suspend ourselves\n"));
			apm_suspends++;
		}
		break;
	case APM_POWER_CHANGE:
		DPRINTF(("power status change\n"));
		apm_power_info(sc, &power);
		if (power.battery_life != APM_BATT_LIFE_UNKNOWN &&
		    power.battery_life < cpu_apmwarn &&
		    (sc->sc_flags & SCFLAG_PRINT) != SCFLAG_NOPRINT &&
		    ((sc->sc_flags & SCFLAG_PRINT) != SCFLAG_PCTPRINT ||
		    sc->sc_batt_life != power.battery_life)) {
			sc->sc_batt_life = power.battery_life;
			apm_power_print(sc, &power);
		}
		apm_record_event(sc, type);
		break;
	case APM_BATTERY_LOW:
		DPRINTF(("Battery low!\n"));
		apm_battlow++;
		apm_record_event(sc, type);
		break;
	default:
		DPRINTF(("apm_handle_event: unsupported event, code %d\n",
		    type));
	}

	return (ret);
}
Exemplo n.º 2
0
static void
apm_resume(struct apm_softc *sc, u_int event_type, u_int event_info)
{
	if (sc->sc_power_state == PWR_RESUME) {
#ifdef APMDEBUG
		aprint_debug_dev(sc->sc_dev, "apm_resume: already running?\n");
#endif
		return;
	}
	sc->sc_power_state = PWR_RESUME;

#ifdef TIMER_FREQ
	/*
	 * Some system requires its clock to be initialized after hybernation.
	 */
	initrtclock(TIMER_FREQ);
#endif

	inittodr(time_second);
	if (!(sc->sc_hwflags & APM_F_DONT_RUN_HOOKS)) {
		splx(apm_spl);
		pmf_system_resume(PMF_Q_NONE);
	}

	apm_record_event(sc, event_type);
}
Exemplo n.º 3
0
void
apm_resume(struct apm_softc *sc, struct apmregs *regs)
{

	apm_resumes = APM_RESUME_HOLDOFF;

	/* lower bit in cx means pccard was powered down */

	apm_record_event(sc, regs->bx);
}
Exemplo n.º 4
0
void
stsec_sensors_update(void *vsc)
{
	struct stsec_softc *sc = (struct stsec_softc *)vsc;
	int status, control, batl, bath;
	ulong batuv;
	struct ksensor *ks;
	uint i;
#if NAPM > 0
	struct apm_power_info old;
	uint cap_pct;
#endif

	for (i = 0; i < nitems(sc->sc_sensors); i++)
		sc->sc_sensors[i].flags |= SENSOR_FINVALID;

	if (stsec_read(sc, ST7_STATUS, &status) != 0 ||
	    stsec_read(sc, ST7_CONTROL, &control) != 0 ||
	    stsec_read(sc, ST7_BATTERY_L, &batl) != 0 ||
	    stsec_read(sc, ST7_BATTERY_H, &bath) != 0)
		return;

	/*
	 * Battery voltage is in 10/1024V units, in the 0-1023 range.
	 */
	batuv = ((ulong)STB_VALUE(bath, batl) * 10 * 1000000) / 1024;
	
	ks = &sc->sc_sensors[STSEC_SENSOR_AC_PRESENCE];
	ks->value = !!ISSET(status, STS_AC_AVAILABLE);
	ks->flags &= ~SENSOR_FINVALID;

	/*
	 * Old mobo design does not have a battery presence bit; the Linux
	 * code decides there is no battery if the reported battery voltage
	 * is too low, we'll do the same.
	 */
	ks = &sc->sc_sensors[STSEC_SENSOR_BATTERY_PRESENCE];
	switch (gdium_revision) {
	case 0:
		if (ISSET(status, STS_AC_AVAILABLE))
			ks->value = batuv > 500000;
		else
			ks->value = 1;
		break;
	default:
		ks->value = !!ISSET(status, STS_BATTERY_PRESENT);
		break;
	}
	ks->flags &= ~SENSOR_FINVALID;
	
	ks = &sc->sc_sensors[STSEC_SENSOR_BATTERY_STATE];
	ks->value = !!ISSET(control, STC_CHARGE_ENABLE);
	ks->flags &= ~SENSOR_FINVALID;
	
	ks = &sc->sc_sensors[STSEC_SENSOR_BATTERY_VOLTAGE];
	ks->value = (int64_t)batuv;
	ks->flags &= ~SENSOR_FINVALID;

#if NAPM > 0
	bcopy(&stsec_apmdata, &old, sizeof(old));

	if (batuv < STSEC_BAT_MIN_VOLT)
		batuv = STSEC_BAT_MIN_VOLT;
	else if (batuv > STSEC_BAT_MAX_VOLT)
		batuv = STSEC_BAT_MAX_VOLT;
	cap_pct = (batuv - STSEC_BAT_MIN_VOLT) * 100 / (STSEC_BAT_MAX_VOLT -
	    STSEC_BAT_MIN_VOLT);
	stsec_apmdata.battery_life = cap_pct;

	stsec_apmdata.ac_state = ISSET(status, STS_AC_AVAILABLE) ? APM_AC_ON :
	    APM_AC_OFF;
	if (!sc->sc_sensors[STSEC_SENSOR_BATTERY_PRESENCE].value) {
		stsec_apmdata.battery_state = APM_BATTERY_ABSENT;
		stsec_apmdata.minutes_left = 0;
		stsec_apmdata.battery_life = 0;
	} else {
		if (ISSET(control, STC_CHARGE_ENABLE))
			stsec_apmdata.battery_state = APM_BATT_CHARGING;
		/* XXX arbitrary */
		else if (cap_pct < 10)
			stsec_apmdata.battery_state = APM_BATT_CRITICAL;
		else if (cap_pct > 60)
			stsec_apmdata.battery_state = APM_BATT_HIGH;
		else
			stsec_apmdata.battery_state = APM_BATT_LOW;

		stsec_apmdata.minutes_left = -1; /* unknown */
	}
	if (old.ac_state != stsec_apmdata.ac_state) 
		apm_record_event(APM_POWER_CHANGE, "AC power",
			stsec_apmdata.ac_state ? "restored" : "lost");
	if (old.battery_state != stsec_apmdata.battery_state) 
		apm_record_event(APM_POWER_CHANGE, "battery",
		    BATTERY_STRING(stsec_apmdata.battery_state));
#endif
}
Exemplo n.º 5
0
int
apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct pxa2x0_apm_softc *sc;
	struct apm_power_info *power;
	int error = 0;

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

	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++;	/* XXX */
		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;
		break;
	case APM_IOC_GETPOWER:
	        power = (struct apm_power_info *)data;
		apm_power_info(sc, power);
		break;
	case APM_IOC_STANDBY_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else if (apm_record_event(sc, APM_USER_STANDBY_REQ))
			error = EINVAL; /* ? */
		break;
	case APM_IOC_SUSPEND_REQ:
		if ((flag & FWRITE) == 0)
			error = EBADF;
		else if (apm_record_event(sc, APM_USER_SUSPEND_REQ))
			error = EINVAL; /* ? */
	default:
		error = ENOTTY;
	}

	return (error);
}
Exemplo n.º 6
0
static void
apm_event_handle(struct apm_softc *sc, u_int event_code, u_int event_info)
{
	int error;
	const char *code;
	struct apm_power_info pi;

	switch (event_code) {
	case APM_USER_STANDBY_REQ:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: user standby request\n"));
		if (apm_do_standby) {
			if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
				apm_userstandbys++;
			apm_op_inprog++;
			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
			    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
		} else {
			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
			    APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
			/* in case BIOS hates being spurned */
			(*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
		}
		break;

	case APM_STANDBY_REQ:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby request\n"));
		if (apm_op_inprog) {
			DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
			    ("damn fool BIOS did not wait for answer\n"));
			/* just give up the fight */
			apm_damn_fool_bios = 1;
		}
		if (apm_do_standby) {
			if (apm_op_inprog == 0 &&
			    apm_record_event(sc, event_code))
				apm_standbys++;
			apm_op_inprog++;
			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
			    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
		} else {
			(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
			    APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED);
			/* in case BIOS hates being spurned */
			(*sc->sc_ops->aa_enable)(sc->sc_cookie, 1);
		}
		break;

	case APM_USER_SUSPEND_REQ:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: user suspend request\n"));
		if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
			apm_suspends++;
		apm_op_inprog++;
		(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
		    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
		break;

	case APM_SUSPEND_REQ:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: system suspend request\n"));
		if (apm_op_inprog) {
			DPRINTF(APMDEBUG_EVENTS | APMDEBUG_ANOM,
			    ("damn fool BIOS did not wait for answer\n"));
			/* just give up the fight */
			apm_damn_fool_bios = 1;
		}
		if (apm_op_inprog == 0 && apm_record_event(sc, event_code))
			apm_suspends++;
		apm_op_inprog++;
		(void)(*sc->sc_ops->aa_set_powstate)(sc->sc_cookie,
		    APM_DEV_ALLDEVS, APM_LASTREQ_INPROG);
		break;

	case APM_POWER_CHANGE:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: power status change\n"));
		error = (*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
#ifdef APM_POWER_PRINT
		/* only print if nobody is catching events. */
		if (error == 0 &&
		    (sc->sc_flags & (SCFLAG_OREAD|SCFLAG_OWRITE)) == 0)
			apm_power_print(sc, &pi);
#else
		__USE(error);
#endif
		apm_record_event(sc, event_code);
		break;

	case APM_NORMAL_RESUME:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: resume system\n"));
		apm_resume(sc, event_code, event_info);
		break;

	case APM_CRIT_RESUME:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: critical resume system"));
		apm_resume(sc, event_code, event_info);
		break;

	case APM_SYS_STANDBY_RESUME:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: system standby resume\n"));
		apm_resume(sc, event_code, event_info);
		break;

	case APM_UPDATE_TIME:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: update time\n"));
		apm_resume(sc, event_code, event_info);
		break;

	case APM_CRIT_SUSPEND_REQ:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: critical system suspend\n"));
		apm_record_event(sc, event_code);
		apm_suspend(sc);
		break;

	case APM_BATTERY_LOW:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: battery low\n"));
		apm_battlow++;
		apm_record_event(sc, event_code);
		break;

	case APM_CAP_CHANGE:
		DPRINTF(APMDEBUG_EVENTS, ("apmev: capability change\n"));
		if (apm_minver < 2) {
			DPRINTF(APMDEBUG_EVENTS, ("apm: unexpected event\n"));
		} else {
			u_int numbatts, capflags;
			(*sc->sc_ops->aa_get_capabilities)(sc->sc_cookie,
			    &numbatts, &capflags);
			(*sc->sc_ops->aa_get_powstat)(sc->sc_cookie, 0, &pi);
		}
		break;

	default:
		switch (event_code >> 8) {
			case 0:
				code = "reserved system";
				break;
			case 1:
				code = "reserved device";
				break;
			case 2:
				code = "OEM defined";
				break;
			default:
				code = "reserved";
				break;
		}
		printf("APM: %s event code %x\n", code, event_code);
	}
}
Exemplo n.º 7
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.º 8
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;
}
Exemplo n.º 9
0
void
ykbec_refresh(void *arg)
{
	struct ykbec_softc *sc = (struct ykbec_softc *)arg;
	u_int val, bat_charge, bat_status, charge_status, bat_state, power_flag;
	u_int cap_pct, fullcap;
	int current;
#if NAPM > 0
	struct apm_power_info old;
#endif

	val = ykbec_read16(sc, REG_FAN_SPEED_HIGH) & 0xfffff;
	if (val != 0) {
		val = KB3310_FAN_SPEED_DIVIDER / val;
		sc->sc_sensor[YKBEC_FAN].value = val;
		CLR(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID);
	} else
		SET(sc->sc_sensor[YKBEC_FAN].flags, SENSOR_FINVALID);

	val = ykbec_read(sc, ECTEMP_CURRENT_REG);
	sc->sc_sensor[YKBEC_ITEMP].value = val * 1000000 + 273150000;

	fullcap = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH);
	sc->sc_sensor[YKBEC_FCAP].value = fullcap * 1000;

	current = ykbec_read16(sc, REG_CURRENT_HIGH);
	/* sign extend short -> int, int -> int64 will be done next statement */
	current |= -(current & 0x8000);
	sc->sc_sensor[YKBEC_BCURRENT].value = -1000 * current;

	sc->sc_sensor[YKBEC_BVOLT].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) *
	    1000;

	val = ykbec_read16(sc, REG_TEMPERATURE_HIGH);
	sc->sc_sensor[YKBEC_BTEMP].value = val * 1000000 + 273150000;

	cap_pct = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH);
	sc->sc_sensor[YKBEC_CAP].value = cap_pct * 1000;

	bat_charge = ykbec_read(sc, REG_BAT_CHARGE);
	bat_status = ykbec_read(sc, REG_BAT_STATUS);
	charge_status = ykbec_read(sc, REG_CHARGE_STATUS);
	bat_state = ykbec_read(sc, REG_BAT_STATE);
	power_flag = ykbec_read(sc, REG_POWER_FLAG);

	sc->sc_sensor[YKBEC_CHARGING].value = !!ISSET(bat_state,
	    BAT_STATE_CHARGING);
	sc->sc_sensor[YKBEC_AC].value = !!ISSET(power_flag,
	    POWER_FLAG_ADAPTER_IN);

	sc->sc_sensor[YKBEC_CAP].status = ISSET(bat_status, BAT_STATUS_BAT_LOW) ?
		SENSOR_S_CRIT : SENSOR_S_OK;

#if NAPM > 0
	bcopy(&ykbec_apmdata, &old, sizeof(old));
	ykbec_apmdata.battery_life = cap_pct;
	ykbec_apmdata.ac_state = ISSET(power_flag, POWER_FLAG_ADAPTER_IN) ?
	    APM_AC_ON : APM_AC_OFF;
	if (!ISSET(bat_status, BAT_STATUS_BAT_EXISTS)) {
		ykbec_apmdata.battery_state = APM_BATTERY_ABSENT;
		ykbec_apmdata.minutes_left = 0;
		ykbec_apmdata.battery_life = 0;
	} else {
		if (ISSET(bat_state, BAT_STATE_CHARGING))
			ykbec_apmdata.battery_state = APM_BATT_CHARGING;
		else if (ISSET(bat_status, BAT_STATUS_BAT_LOW))
			ykbec_apmdata.battery_state = APM_BATT_CRITICAL;
		/* XXX arbitrary */
		else if (cap_pct > 60)
			ykbec_apmdata.battery_state = APM_BATT_HIGH;
		else
			ykbec_apmdata.battery_state = APM_BATT_LOW;

		/* if charging, current is positive */
		if (ISSET(bat_state, BAT_STATE_CHARGING))
			current = 0;
		else
			current = -current;
		/* XXX Yeeloong draw is about 1A */
		if (current <= 0)
			current = 1000;
		/* XXX at 5?%, the Yeeloong shuts down */
		if (cap_pct <= 5)
			cap_pct = 0;
		else
			cap_pct -= 5;
		fullcap = cap_pct * 60 * fullcap / 100;
		ykbec_apmdata.minutes_left = fullcap / current;

	}
	if (old.ac_state != ykbec_apmdata.ac_state) 
		apm_record_event(APM_POWER_CHANGE, "AC power",
			ykbec_apmdata.ac_state ? "restored" : "lost");
	if (old.battery_state != ykbec_apmdata.battery_state) 
		apm_record_event(APM_POWER_CHANGE, "battery",
		    BATTERY_STRING(ykbec_apmdata.battery_state));
#endif
}