595a08978c
Use ACPI_SUCCESS/ACPI_FAILURE consistently. The AcpiGetInto* interfaces are obsoleted by ACPI_ALLOCATE_BUFFER. Kill off the timeouts that used to read _BIF and _BST. These are invoked when the battery is actually read. timeout() is dangerous in combination with ACPI, as the interpreter can block. This driver still needs more work.
628 lines
15 KiB
C
628 lines
15 KiB
C
/*-
|
|
* Copyright (c) 2000 Munehiro Matsuda
|
|
* Copyright (c) 2000 Takanori Watanabe
|
|
* Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include "opt_acpi.h"
|
|
#include <sys/param.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/ioccom.h>
|
|
#include <sys/conf.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/resource.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/malloc.h>
|
|
|
|
#include "acpi.h"
|
|
|
|
#include <dev/acpica/acpivar.h>
|
|
#include <dev/acpica/acpiio.h>
|
|
|
|
MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data");
|
|
|
|
#define CMBAT_POLLRATE (60 * hz)
|
|
|
|
/*
|
|
* Hooks for the ACPI CA debugging infrastructure
|
|
*/
|
|
#define _COMPONENT ACPI_BATTERY
|
|
ACPI_MODULE_NAME("BATTERY")
|
|
|
|
#define ACPI_BATTERY_BST_CHANGE 0x80
|
|
#define ACPI_BATTERY_BIF_CHANGE 0x81
|
|
|
|
#define PKG_GETINT(res, tmp, idx, dest, label) do { \
|
|
tmp = &res->Package.Elements[idx]; \
|
|
if (tmp == NULL) { \
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
|
|
__func__ ": PKG_GETINT error, idx = %d\n.", idx); \
|
|
goto label; \
|
|
} \
|
|
if (tmp->Type != ACPI_TYPE_INTEGER) \
|
|
goto label; \
|
|
dest = tmp->Integer.Value; \
|
|
} while (0)
|
|
|
|
#define PKG_GETSTR(res, tmp, idx, dest, size, label) do { \
|
|
size_t length; \
|
|
length = size; \
|
|
tmp = &res->Package.Elements[idx]; \
|
|
if (tmp == NULL) { \
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
|
|
__func__ ": PKG_GETSTR error, idx = %d\n.", idx); \
|
|
goto label; \
|
|
} \
|
|
bzero(dest, sizeof(dest)); \
|
|
switch (tmp->Type) { \
|
|
case ACPI_TYPE_STRING: \
|
|
if (tmp->String.Length < length) { \
|
|
length = tmp->String.Length; \
|
|
} \
|
|
strncpy(dest, tmp->String.Pointer, length); \
|
|
break; \
|
|
case ACPI_TYPE_BUFFER: \
|
|
if (tmp->Buffer.Length < length) { \
|
|
length = tmp->Buffer.Length; \
|
|
} \
|
|
strncpy(dest, tmp->Buffer.Pointer, length); \
|
|
break; \
|
|
default: \
|
|
goto label; \
|
|
} \
|
|
dest[sizeof(dest)-1] = '\0'; \
|
|
} while (0)
|
|
|
|
struct acpi_cmbat_softc {
|
|
device_t dev;
|
|
|
|
struct acpi_bif bif;
|
|
struct acpi_bst bst;
|
|
struct timespec bif_lastupdated;
|
|
struct timespec bst_lastupdated;
|
|
int bif_updating;
|
|
int bst_updating;
|
|
|
|
int not_present;
|
|
int cap;
|
|
int min;
|
|
int full_charge_time;
|
|
};
|
|
|
|
static struct timespec acpi_cmbat_info_lastupdated;
|
|
|
|
/* XXX: devclass_get_maxunit() don't give us the current allocated units... */
|
|
static int acpi_cmbat_units = 0;
|
|
|
|
static int acpi_cmbat_info_expired(struct timespec *);
|
|
static void acpi_cmbat_info_updated(struct timespec *);
|
|
static void acpi_cmbat_get_bst(void *);
|
|
static void acpi_cmbat_get_bif(void *);
|
|
static void acpi_cmbat_notify_handler(ACPI_HANDLE, UINT32, void *);
|
|
static int acpi_cmbat_probe(device_t);
|
|
static int acpi_cmbat_attach(device_t);
|
|
static int acpi_cmbat_resume(device_t);
|
|
static int acpi_cmbat_ioctl(u_long, caddr_t, void *);
|
|
static int acpi_cmbat_get_total_battinfo(struct acpi_battinfo *);
|
|
|
|
static __inline int
|
|
acpi_cmbat_info_expired(struct timespec *lastupdated)
|
|
{
|
|
struct timespec curtime;
|
|
|
|
if (lastupdated == NULL) {
|
|
return (1);
|
|
}
|
|
|
|
if (!timespecisset(lastupdated)) {
|
|
return (1);
|
|
}
|
|
|
|
getnanotime(&curtime);
|
|
timespecsub(&curtime, lastupdated);
|
|
return ((curtime.tv_sec < 0 || curtime.tv_sec > acpi_battery_get_info_expire()));
|
|
}
|
|
|
|
|
|
static __inline void
|
|
acpi_cmbat_info_updated(struct timespec *lastupdated)
|
|
{
|
|
|
|
if (lastupdated != NULL) {
|
|
getnanotime(lastupdated);
|
|
}
|
|
}
|
|
|
|
static void
|
|
acpi_cmbat_get_bst(void *context)
|
|
{
|
|
device_t dev;
|
|
struct acpi_cmbat_softc *sc;
|
|
ACPI_STATUS as;
|
|
ACPI_OBJECT *res, *tmp;
|
|
ACPI_HANDLE h;
|
|
ACPI_BUFFER bst_buffer;
|
|
|
|
dev = context;
|
|
sc = device_get_softc(dev);
|
|
h = acpi_get_handle(dev);
|
|
bst_buffer.Pointer = NULL;
|
|
|
|
if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) {
|
|
return;
|
|
}
|
|
|
|
if (sc->bst_updating) {
|
|
return;
|
|
}
|
|
sc->bst_updating = 1;
|
|
|
|
bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
|
|
if (ACPI_FAILURE(as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer))) {
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
"error fetching current battery status -- %s\n",
|
|
AcpiFormatException(as));
|
|
goto end;
|
|
}
|
|
|
|
res = (ACPI_OBJECT *)bst_buffer.Pointer;
|
|
|
|
if ((res->Type != ACPI_TYPE_PACKAGE) || (res->Package.Count != 4)) {
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
"battery status corrupted\n");
|
|
goto end;
|
|
}
|
|
|
|
PKG_GETINT(res, tmp, 0, sc->bst.state, end);
|
|
PKG_GETINT(res, tmp, 1, sc->bst.rate, end);
|
|
PKG_GETINT(res, tmp, 2, sc->bst.cap, end);
|
|
PKG_GETINT(res, tmp, 3, sc->bst.volt, end);
|
|
acpi_cmbat_info_updated(&sc->bst_lastupdated);
|
|
end:
|
|
if (bst_buffer.Pointer != NULL)
|
|
AcpiOsFree(bst_buffer.Pointer);
|
|
sc->bst_updating = 0;
|
|
}
|
|
|
|
static void
|
|
acpi_cmbat_get_bif(void *context)
|
|
{
|
|
device_t dev;
|
|
struct acpi_cmbat_softc *sc;
|
|
ACPI_STATUS as;
|
|
ACPI_OBJECT *res, *tmp;
|
|
ACPI_HANDLE h;
|
|
ACPI_BUFFER bif_buffer;
|
|
|
|
dev = context;
|
|
sc = device_get_softc(dev);
|
|
h = acpi_get_handle(dev);
|
|
bif_buffer.Pointer = NULL;
|
|
|
|
if (!acpi_cmbat_info_expired(&sc->bif_lastupdated)) {
|
|
return;
|
|
}
|
|
|
|
if (sc->bif_updating) {
|
|
return;
|
|
}
|
|
sc->bif_updating = 1;
|
|
|
|
bif_buffer.Length = ACPI_ALLOCATE_BUFFER;
|
|
if (ACPI_FAILURE(as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer))) {
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
"error fetching current battery info -- %s\n",
|
|
AcpiFormatException(as));
|
|
goto end;
|
|
}
|
|
|
|
res = (ACPI_OBJECT *)bif_buffer.Pointer;
|
|
|
|
if ((res->Type != ACPI_TYPE_PACKAGE) || (res->Package.Count != 13)) {
|
|
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
|
|
"battery info corrupted\n");
|
|
goto end;
|
|
}
|
|
|
|
PKG_GETINT(res, tmp, 0, sc->bif.unit, end);
|
|
PKG_GETINT(res, tmp, 1, sc->bif.dcap, end);
|
|
PKG_GETINT(res, tmp, 2, sc->bif.lfcap, end);
|
|
PKG_GETINT(res, tmp, 3, sc->bif.btech, end);
|
|
PKG_GETINT(res, tmp, 4, sc->bif.dvol, end);
|
|
PKG_GETINT(res, tmp, 5, sc->bif.wcap, end);
|
|
PKG_GETINT(res, tmp, 6, sc->bif.lcap, end);
|
|
PKG_GETINT(res, tmp, 7, sc->bif.gra1, end);
|
|
PKG_GETINT(res, tmp, 8, sc->bif.gra2, end);
|
|
PKG_GETSTR(res, tmp, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN, end);
|
|
PKG_GETSTR(res, tmp, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN, end);
|
|
PKG_GETSTR(res, tmp, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN, end);
|
|
PKG_GETSTR(res, tmp, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN, end);
|
|
acpi_cmbat_info_updated(&sc->bif_lastupdated);
|
|
end:
|
|
if (bif_buffer.Pointer != NULL)
|
|
AcpiOsFree(bif_buffer.Pointer);
|
|
sc->bif_updating = 0;
|
|
}
|
|
|
|
static void
|
|
acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
|
|
{
|
|
device_t dev;
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
dev = (device_t)context;
|
|
if ((sc = device_get_softc(dev)) == NULL) {
|
|
return;
|
|
}
|
|
|
|
switch (notify) {
|
|
case ACPI_BATTERY_BST_CHANGE:
|
|
timespecclear(&sc->bst_lastupdated);
|
|
break;
|
|
case ACPI_BATTERY_BIF_CHANGE:
|
|
timespecclear(&sc->bif_lastupdated);
|
|
AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
acpi_cmbat_probe(device_t dev)
|
|
{
|
|
|
|
if ((acpi_get_type(dev) == ACPI_TYPE_DEVICE) &&
|
|
!acpi_disabled("cmbat") &&
|
|
acpi_MatchHid(dev, "PNP0C0A")) {
|
|
/*
|
|
* Set device description.
|
|
*/
|
|
device_set_desc(dev, "Control method Battery");
|
|
return (0);
|
|
}
|
|
return (ENXIO);
|
|
}
|
|
|
|
static int
|
|
acpi_cmbat_attach(device_t dev)
|
|
{
|
|
int error;
|
|
ACPI_HANDLE handle;
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
if ((sc = device_get_softc(dev)) == NULL) {
|
|
return (ENXIO);
|
|
}
|
|
|
|
handle = acpi_get_handle(dev);
|
|
|
|
AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY,
|
|
acpi_cmbat_notify_handler, dev);
|
|
|
|
sc->bif_updating = sc->bst_updating = 0;
|
|
sc->dev = dev;
|
|
|
|
timespecclear(&sc->bif_lastupdated);
|
|
timespecclear(&sc->bst_lastupdated);
|
|
|
|
if (acpi_cmbat_units == 0) {
|
|
if ((error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BIF,
|
|
acpi_cmbat_ioctl, NULL)) != 0) {
|
|
return (error);
|
|
}
|
|
if ((error = acpi_register_ioctl(ACPIIO_CMBAT_GET_BST,
|
|
acpi_cmbat_ioctl, NULL)) != 0) {
|
|
return (error);
|
|
}
|
|
}
|
|
|
|
if ((error = acpi_battery_register(ACPI_BATT_TYPE_CMBAT,
|
|
acpi_cmbat_units)) != 0) {
|
|
return (error);
|
|
}
|
|
|
|
acpi_cmbat_units++;
|
|
timespecclear(&acpi_cmbat_info_lastupdated);
|
|
|
|
AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
acpi_cmbat_resume(device_t dev)
|
|
{
|
|
|
|
AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_get_bif, dev);
|
|
return (0);
|
|
}
|
|
|
|
static device_method_t acpi_cmbat_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, acpi_cmbat_probe),
|
|
DEVMETHOD(device_attach, acpi_cmbat_attach),
|
|
DEVMETHOD(device_resume, acpi_cmbat_resume),
|
|
|
|
{0, 0}
|
|
};
|
|
|
|
static driver_t acpi_cmbat_driver = {
|
|
"acpi_cmbat",
|
|
acpi_cmbat_methods,
|
|
sizeof(struct acpi_cmbat_softc),
|
|
};
|
|
|
|
static devclass_t acpi_cmbat_devclass;
|
|
DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0);
|
|
|
|
static int
|
|
acpi_cmbat_ioctl(u_long cmd, caddr_t addr, void *arg)
|
|
{
|
|
device_t dev;
|
|
union acpi_battery_ioctl_arg *ioctl_arg;
|
|
struct acpi_cmbat_softc *sc;
|
|
struct acpi_bif *bifp;
|
|
struct acpi_bst *bstp;
|
|
|
|
ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
|
|
if ((dev = devclass_get_device(acpi_cmbat_devclass,
|
|
ioctl_arg->unit)) == NULL) {
|
|
return (ENXIO);
|
|
}
|
|
|
|
if ((sc = device_get_softc(dev)) == NULL) {
|
|
return (ENXIO);
|
|
}
|
|
|
|
switch (cmd) {
|
|
case ACPIIO_CMBAT_GET_BIF:
|
|
acpi_cmbat_get_bif(dev);
|
|
bifp = &ioctl_arg->bif;
|
|
bifp->unit = sc->bif.unit;
|
|
bifp->dcap = sc->bif.dcap;
|
|
bifp->lfcap = sc->bif.lfcap;
|
|
bifp->btech = sc->bif.btech;
|
|
bifp->dvol = sc->bif.dvol;
|
|
bifp->wcap = sc->bif.wcap;
|
|
bifp->lcap = sc->bif.lcap;
|
|
bifp->gra1 = sc->bif.gra1;
|
|
bifp->gra2 = sc->bif.gra2;
|
|
strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
|
|
strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
|
|
strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
|
|
strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
|
|
break;
|
|
|
|
case ACPIIO_CMBAT_GET_BST:
|
|
acpi_cmbat_get_bst(dev);
|
|
bstp = &ioctl_arg->bst;
|
|
bstp->state = sc->bst.state;
|
|
bstp->rate = sc->bst.rate;
|
|
bstp->cap = sc->bst.cap;
|
|
bstp->volt = sc->bst.volt;
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
acpi_cmbat_get_total_battinfo(struct acpi_battinfo *battinfo)
|
|
{
|
|
int i;
|
|
int error;
|
|
int batt_stat;
|
|
int valid_rate, valid_units;
|
|
int cap, min;
|
|
int total_cap, total_min, total_full;
|
|
device_t dev;
|
|
struct acpi_cmbat_softc *sc;
|
|
static int bat_units = 0;
|
|
static struct acpi_cmbat_softc **bat = NULL;
|
|
|
|
cap = min = -1;
|
|
batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
|
|
error = 0;
|
|
|
|
/* Allocate array of softc pointers */
|
|
if (bat_units != acpi_cmbat_units) {
|
|
if (bat != NULL) {
|
|
free(bat, M_ACPICMBAT);
|
|
bat = NULL;
|
|
}
|
|
bat_units = 0;
|
|
}
|
|
if (bat == NULL) {
|
|
bat_units = acpi_cmbat_units;
|
|
bat = malloc(sizeof(struct acpi_cmbat_softc *) * bat_units,
|
|
M_ACPICMBAT, M_NOWAIT);
|
|
if (bat == NULL) {
|
|
error = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/* Collect softc pointers */
|
|
for (i = 0; i < acpi_cmbat_units; i++) {
|
|
if ((dev = devclass_get_device(acpi_cmbat_devclass, i)) == NULL) {
|
|
error = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
if ((sc = device_get_softc(dev)) == NULL) {
|
|
error = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
bat[i] = sc;
|
|
}
|
|
}
|
|
|
|
/* Get battery status, valid rate and valid units */
|
|
batt_stat = valid_rate = valid_units = 0;
|
|
for (i = 0; i < acpi_cmbat_units; i++) {
|
|
bat[i]->not_present = 0;
|
|
acpi_cmbat_get_bst(bat[i]->dev);
|
|
|
|
/* If battey not installed, we get strange values */
|
|
if (bat[i]->bst.state >= ACPI_BATT_STAT_MAX ||
|
|
bat[i]->bst.cap == 0xffffffff ||
|
|
bat[i]->bst.volt == 0xffffffff ||
|
|
bat[i]->bif.lfcap == 0) {
|
|
bat[i]->not_present = 1;
|
|
continue;
|
|
}
|
|
|
|
valid_units++;
|
|
|
|
bat[i]->cap = 100 * bat[i]->bst.cap / bat[i]->bif.lfcap;
|
|
|
|
batt_stat |= bat[i]->bst.state;
|
|
|
|
if (bat[i]->bst.rate > 0) {
|
|
/*
|
|
* XXX Hack to calculate total battery time.
|
|
* Systems with 2 or more battries, they may get used
|
|
* one by one, thus bst.rate is set only to the one
|
|
* in use. For remaining batteries bst.rate = 0, which
|
|
* makes it impossible to calculate remaining time.
|
|
* Some other systems may need sum of bst.rate in
|
|
* dis-charging state.
|
|
* There for we sum up the bst.rate that is valid
|
|
* (in dis-charging state), and use the sum to
|
|
* calcutate remaining batteries' time.
|
|
*/
|
|
if (bat[i]->bst.state & ACPI_BATT_STAT_DISCHARG) {
|
|
valid_rate += bat[i]->bst.rate;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Calculate total battery capacity and time */
|
|
total_cap = total_min = total_full = 0;
|
|
for (i = 0; i < acpi_cmbat_units; i++) {
|
|
if (bat[i]->not_present) {
|
|
continue;
|
|
}
|
|
|
|
if (valid_rate > 0) {
|
|
/* Use the sum of bst.rate */
|
|
bat[i]->min = 60 * bat[i]->bst.cap / valid_rate;
|
|
} else if (bat[i]->full_charge_time > 0) {
|
|
bat[i]->min = (bat[i]->full_charge_time * bat[i]->cap) / 100;
|
|
} else {
|
|
/* Couldn't find valid rate and full battery time */
|
|
bat[i]->min = 0;
|
|
}
|
|
total_min += bat[i]->min;
|
|
total_cap += bat[i]->cap;
|
|
total_full += bat[i]->full_charge_time;
|
|
}
|
|
|
|
/* Battery life */
|
|
if (valid_units == 0) {
|
|
cap = -1;
|
|
batt_stat = ACPI_BATT_STAT_NOT_PRESENT;
|
|
} else {
|
|
cap = total_cap / valid_units;
|
|
}
|
|
|
|
/* Battery time */
|
|
if (valid_units == 0) {
|
|
min = -1;
|
|
} else if (valid_rate == 0 || (batt_stat & ACPI_BATT_STAT_CHARGING)) {
|
|
if (total_full == 0) {
|
|
min = -1;
|
|
} else {
|
|
min = (total_full * cap) / 100;
|
|
}
|
|
} else {
|
|
min = total_min;
|
|
}
|
|
|
|
acpi_cmbat_info_updated(&acpi_cmbat_info_lastupdated);
|
|
out:
|
|
battinfo->cap = cap;
|
|
battinfo->min = min;
|
|
battinfo->state = batt_stat;
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Public interfaces.
|
|
*/
|
|
|
|
int
|
|
acpi_cmbat_get_battinfo(int unit, struct acpi_battinfo *battinfo)
|
|
{
|
|
int error;
|
|
device_t dev;
|
|
struct acpi_cmbat_softc *sc;
|
|
|
|
if (unit == -1) {
|
|
return (acpi_cmbat_get_total_battinfo(battinfo));
|
|
}
|
|
|
|
if (acpi_cmbat_info_expired(&acpi_cmbat_info_lastupdated)) {
|
|
error = acpi_cmbat_get_total_battinfo(battinfo);
|
|
if (error) {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
error = 0;
|
|
if (unit >= acpi_cmbat_units) {
|
|
error = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
if ((dev = devclass_get_device(acpi_cmbat_devclass, unit)) == NULL) {
|
|
error = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
if ((sc = device_get_softc(dev)) == NULL) {
|
|
error = ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
if (sc->not_present) {
|
|
battinfo->cap = -1;
|
|
battinfo->min = -1;
|
|
battinfo->state = ACPI_BATT_STAT_NOT_PRESENT;
|
|
} else {
|
|
battinfo->cap = sc->cap;
|
|
battinfo->min = sc->min;
|
|
battinfo->state = sc->bst.state;
|
|
}
|
|
out:
|
|
return (error);
|
|
}
|
|
|