Add _BIX (Battery Information Extended) object support.

ACPI Control Method Batteries have a _BIF and/or _BIX object which
provide static properties of the battery.  FreeBSD acpi_cmbat module
supported _BIF object only, which was deprecated as of ACPI 4.0.
_BIX is an extended version of _BIF defined in ACPI 4.0 or later.

As of writing, _BIX has two revisions.  One is in ACPI 4.0 (rev.0) and
another is in ACPI 6.0 (rev.1).  It seems that hardware vendors still
stick to _BIF only or _BIX rev.0 + _BIF for the maximum compatibility.
Microsoft requires _BIX rev.0 for Windows machines, so there are some
laptop machines with _BIX rev.0 only. In this case, FreeBSD does not
recognize the battery information.

After this change, the acpi_cmbat module gets battery information from
_BIX or _BIF object and internally uses _BIX rev.1 data structure as
the primary information store in the kernel.  ACPIIO_BATT_GET_BI[FX]
returns an acpi_bi[fx] structure built by using information obtained
from a _BIF or a _BIX object found on the system.  The revision number
field can be used to check which field is available.  The acpiconf(8)
utility will show additional information if _BIX is available.

Although ABIs of ACPIIO_BATT_* were changed, the existing APIs for
userland utilities are not changed and the backward-compatible ABIs
are provided.  This means that older versions of acpiconf(8) can also
work with the new kernel. The (union acpi_battery_ioctl_arg) was
padded to 256 byte long to avoid another ABI change in the future.
A _BIX object with its revision number >1 will be treated as
compatible with the rev.1 _BIX format.

Reviewed by:	takawata
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D23728
This commit is contained in:
Hiroki Sato 2020-02-19 06:28:55 +00:00
parent 9fab908a79
commit 294de6bbd6
9 changed files with 559 additions and 229 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd November 26, 2019
.Dd February 16, 2020
.Dt ACPI_BATTERY 4
.Os
.Sh NAME
@ -37,6 +37,7 @@
The
.Nm
is a driver for battery management features of the ACPI module.
.Pp
An ACPI-compatible battery device supports either a Control
Method Battery interface or a Smart Battery subsystem interface.
The former is accessed by the AML
@ -45,7 +46,8 @@ code control methods,
and the latter is controlled directly through the ACPI EC
.Pq Embedded Controller
typically via an SMBus interface.
This driver supports the
.Pp
This driver supports the
.Xr sysctl 8
and
.Xr ioctl 2
@ -59,60 +61,86 @@ driver takes a single integer value for the battery unit
number as an argument,
and returns a specific structure for each request.
A special unit number
.Li ACPI_BATTERY_ALL_UNITS
.Dv ACPI_BATTERY_ALL_UNITS
specifies all of the attached units
and reports accumulated information.
.Bl -tag -width indent
.It ACPIIO_BATT_GET_UNITS Vt int
.It Dv ACPIIO_BATT_GET_UNITS Vt int
Returns the number of battery units in the system.
The unit number argument will be ignored.
.It ACPIIO_BATT_GET_BATTINFO Vt struct acpi_battinfo
.It Dv ACPIIO_BATT_GET_BATTINFO Vt struct acpi_battinfo
Returns the following:
.Bl -tag -width indent
.It cap
.It Va cap
Battery capacity in percent,
.It min
.It Va min
Remaining battery life in minutes,
.It state
.It Va state
Current status of the battery encoded in the following:
.Bl -tag -width indent
.It ACPI_BATT_STAT_DISCHARG Pq 0x0001
.It Dv ACPI_BATT_STAT_DISCHARG Pq 0x0001
Battery is discharging,
.It ACPI_BATT_STAT_CHARGING Pq 0x0002
.It Dv ACPI_BATT_STAT_CHARGING Pq 0x0002
Battery is being charged, or
.It ACPI_BATT_STAT_CRITICAL Pq 0x0004
.It Dv ACPI_BATT_STAT_CRITICAL Pq 0x0004
Remaining battery life is critically low.
.El
.Pp
Note that the status bits of each battery will be
consolidated when
.Li ACPI_BATTERY_ALL_UNITS
.Dv ACPI_BATTERY_ALL_UNITS
is specified.
.It rate
.It Va rate
Current battery discharging rate in mW.
.Li -1
means not discharging right now.
.El
.It ACPIIO_BATT_GET_BIF Vt struct acpi_bif
.It Dv ACPIIO_BATT_GET_BIX Vt struct acpi_bix
Returns battery information given by the ACPI
.Li _BIF Pq Battery Information
.Li _BIX Pq Battery Information
object,
which is the static portion of the Control Method
Battery information.
In the case of a Smart Battery attached to SMBus,
In the case of a Smart Battery attached to
SMBus or a Control Method Battery with a
.Li _BIF
object,
this ioctl will build a
.Vt struct acpi_bif
.Vt struct acpi_bix
structure based on the obtained information
and return it.
.Bl -tag -width indent
.It units
.It Va rev
Revision number of the object.
There are the following well-known values:
.Bl -tag -width indent
.It Dv ACPI_BIX_REV_0 Pq 0x0000
A
.Li _BIX
object in ACPI 4.0.
.It Dv ACPI_BIX_REV_1 Pq 0x0001
A
.Li _BIX
object in ACPI 6.0.
.It Dv ACPI_BIX_REV_BIF Pq 0xffff
A
.Li _BIX
object built from the
.Li _BIF
object found on the system.
.El
.Pp
Note that this field should be checked by using
.Fn ACPI_BIX_REV_MIN_CHECK var rev
macro when checking the minimum revision number.
.It Va units
Indicates the units used by the battery to report its
capacity and charge rate encoded in the following:
.Bl -tag -width indent
.It ACPI_BIF_UNITS_MW Pq 0x00000000
.It ACPI_BIX_UNITS_MW Pq 0x00000000
in mW
.Pq power
.It ACPI_BIF_UNITS_MA Pq 0x00000001
.It ACPI_BIX_UNITS_MA Pq 0x00000001
in mA
.Pq current
.El
@ -120,31 +148,69 @@ in mA
Note that capacity is expressed in mWh or mAh,
and rate is expressed in mW or mA,
respectively.
.It dcap
.It Va dcap
The Battery's design capacity,
which is the nominal capacity of a new battery.
This is expressed as power or current depending on
the value of
.Va units .
.It lfcap
.It Va lfcap
Predicted battery capacity when fully charged.
Typically this will decrease every charging cycle.
.It btech
Battery technology:
.Bl -tag -width indent
.It 0x00000000 Primary cell Pq non-rechargable
.It 0x00000001 Secondery cell Pq rechargable
.It 0x00000001 Secondary cell Pq rechargable
.El
.It dvol
.It Va dvol
Design voltage in mV,
which is the nominal voltage of a new battery.
.It wcap
.It Va wcap
Design capacity of warning.
When a discharging battery device reaches this capacity,
notification is sent to the system.
.It lcap
.It Va lcap
Design capacity of low.
.It gra1
.It Va cycles
.Pq rev 0 or newer
The number of cycles the battery has experienced.
A cycle means an amount of discharge occurred which was
approximately equal to the value of Design Capacity.
.It Va accuracy
.Pq rev 0 or newer
The accuracy of the battery capacity measurement,
in thousandth of a percent.
.It Va stmax
.Pq rev 0 or newer
The Maximum Sampling Time of the battery in
milliseconds.
This is the maximum duration between two consecutive
measurements of the battery's capacities specified in
.Li _BST .
If two succeeding readings of
.Li _BST
beyond this duration occur,
two different results can be returned.
.It Va stmin
.Pq rev 0 or newer
The Minimum Sampling Time of the battery in
milliseconds.
.It Va aimax
.Pq rev 0 or newer
The Maximum Average Interval of the battery in
milliseconds.
This is the length of time within which the battery
averages the capacity measurements specified in
.Li _BST .
The Sampling Time specifies the frequency of measurements,
and the Average Interval specifies the width of the time
window of every measurement.
.It Va aimin
.Pq rev 0 or newer
The Minimum Average Interval of the battery in
milliseconds.
.It Va gra1
Battery capacity granularity between
.Va low
and
@ -152,7 +218,7 @@ and
This is expressed as power or current depending on
the value of
.Va units .
.It gra2
.It Va gra2
Battery capacity granularity between
.Va warning
and
@ -160,15 +226,41 @@ and
This is expressed as power or current depending on
the value of
.Va units .
.It model
.It Va model
Model number of the battery as a string.
.It serial
.It Va serial
Serial number of the battery as a string.
.It type
.It Va type
Type identifier of the battery as a string.
.It oeminfo
.It Va oeminfo
OEM-specific information of the battery as a string.
.It Va scap
.Pq rev 1 or newer
Battery swapping capability encoded in the following:
.Bl -tag -width indent
.It ACPI_BIX_SCAP_NO Pq 0x00000000
Non-swappable
.It ACPI_BIX_SCAP_COLD Pq 0x00000001
Cold-swappable
.It ACPI_BIX_SCAP_HOT Pq 0x00000010
Hot-swappable
.El
.El
.It Dv ACPIIO_BATT_GET_BIF Vt struct acpi_bif
.Pq deprecated
Returns battery information given by the ACPI
.Li _BIF Pq Battery Information
object,
which was deprecated in ACPI 4.0 specification.
The data structure is a subset of
.Vt struct acpi_bix .
.Pp
Note that this ioctl will built a
.Vt struct acpi_bif
structure based on the obtained information
even if this object is not available and a
.Li _BIX
object is found.
.It ACPIIO_BATT_GET_BST Vt struct acpi_bst
Returns battery information given by the ACPI
.Li _BST Pq Battery Status
@ -180,25 +272,25 @@ this ioctl will build a
structure based on the obtained information
and return it.
.Bl -tag -width indent
.It state
.It Va state
Battery state.
The value is encoded in the same way as
.Va state
of
.Vt struct acpi_battinfo .
.It rate
.It Va rate
Battery present rate of charging or discharging.
The unit of the value depends on
.Va unit
of
.Vt struct acpi_bif .
.It cap
.It Va cap
Battery remaining capacity.
The unit of this value depends on
.Va unit
of
.Vt struct acpi_bif .
.It volt
.It Va volt
Battery present voltage.
.El
.El
@ -212,6 +304,8 @@ connected batteries:
.It Va hw.acpi.battery.info_expire
Information cache expiration time in seconds.
The battery information obtained by
.Li _BIX
or
.Li _BIF
object will be stored and reused for successive
read access to this MIB within the specified period.
@ -276,8 +370,11 @@ Battery information was changed.
.An Munehiro Matsuda ,
.An Takanori Watanabe Aq Mt takawata@FreeBSD.org ,
.An Mitsuru IWASAKI Aq Mt iwasaki@FreeBSD.org ,
.An Hans Petter Selasky Aq Mt hselasky@FreeBSD.org ,
and
.An Hans Petter Selasky Aq Mt hselasky@FreeBSD.org .
.An Hiroki Sato Aq Mt hrs@FreeBSD.org .
.Pp
This manual page was written by
.An Takanori Watanabe Aq Mt takawata@FreeBSD.org .
.An Takanori Watanabe Aq Mt takawata@FreeBSD.org
and
.An Hiroki Sato Aq Mt hrs@FreeBSD.org .

View File

@ -44,7 +44,7 @@ __FBSDID("$FreeBSD$");
/* Default seconds before re-sampling the battery state. */
#define ACPI_BATTERY_INFO_EXPIRE 5
static int acpi_batteries_initted;
static int acpi_batteries_initialized;
static int acpi_battery_info_expire = ACPI_BATTERY_INFO_EXPIRE;
static struct acpi_battinfo acpi_battery_battinfo;
static struct sysctl_ctx_list acpi_battery_sysctl_ctx;
@ -65,11 +65,10 @@ acpi_battery_register(device_t dev)
{
int error;
error = 0;
ACPI_SERIAL_BEGIN(battery);
if (!acpi_batteries_initted)
error = acpi_battery_init();
error = acpi_battery_init();
ACPI_SERIAL_END(battery);
return (error);
}
@ -107,11 +106,12 @@ acpi_battery_bst_valid(struct acpi_bst *bst)
bst->cap != ACPI_BATT_UNKNOWN && bst->volt != ACPI_BATT_UNKNOWN);
}
/* Check _BIF results for validity. */
/* Check _BI[FX] results for validity. */
int
acpi_battery_bif_valid(struct acpi_bif *bif)
acpi_battery_bix_valid(struct acpi_bix *bix)
{
return (bif->lfcap != 0);
return (bix->lfcap != 0);
}
/* Get info about one or all batteries. */
@ -123,7 +123,7 @@ acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo)
devclass_t batt_dc;
device_t batt_dev;
struct acpi_bst *bst;
struct acpi_bif *bif;
struct acpi_bix *bix;
struct acpi_battinfo *bi;
/*
@ -139,11 +139,11 @@ acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo)
/*
* Allocate storage for all _BST data, their derived battinfo data,
* and the current battery's _BIF data.
* and the current battery's _BIX (or _BIF) data.
*/
bst = malloc(devcount * sizeof(*bst), M_TEMP, M_WAITOK | M_ZERO);
bi = malloc(devcount * sizeof(*bi), M_TEMP, M_WAITOK | M_ZERO);
bif = malloc(sizeof(*bif), M_TEMP, M_WAITOK | M_ZERO);
bix = malloc(sizeof(*bix), M_TEMP, M_WAITOK | M_ZERO);
/*
* Pass 1: for each battery that is present and valid, get its status,
@ -173,12 +173,12 @@ acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo)
* Be sure we can get various info from the battery.
*/
if (ACPI_BATT_GET_STATUS(batt_dev, &bst[i]) != 0 ||
ACPI_BATT_GET_INFO(batt_dev, bif) != 0)
ACPI_BATT_GET_INFO(batt_dev, bix, sizeof(*bix)) != 0)
continue;
/* If a battery is not installed, we sometimes get strange values. */
if (!acpi_battery_bst_valid(&bst[i]) ||
!acpi_battery_bif_valid(bif))
!acpi_battery_bix_valid(bix))
continue;
/*
@ -197,18 +197,18 @@ acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo)
* is 0 (due to some error reading the battery), skip this
* conversion.
*/
if (bif->units == ACPI_BIF_UNITS_MA && bif->dvol != 0 && dev == NULL) {
bst[i].rate = (bst[i].rate * bif->dvol) / 1000;
bst[i].cap = (bst[i].cap * bif->dvol) / 1000;
bif->lfcap = (bif->lfcap * bif->dvol) / 1000;
if (bix->units == ACPI_BIX_UNITS_MA && bix->dvol != 0 && dev == NULL) {
bst[i].rate = (bst[i].rate * bix->dvol) / 1000;
bst[i].cap = (bst[i].cap * bix->dvol) / 1000;
bix->lfcap = (bix->lfcap * bix->dvol) / 1000;
}
/*
* The calculation above may set bif->lfcap to zero. This was
* The calculation above may set bix->lfcap to zero. This was
* seen on a laptop with a broken battery. The result of the
* division was rounded to zero.
*/
if (!acpi_battery_bif_valid(bif))
if (!acpi_battery_bix_valid(bix))
continue;
/*
@ -216,16 +216,16 @@ acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo)
* "real-capacity" when the battery is fully charged. That breaks
* the above arithmetic as it needs to be 100% maximum.
*/
if (bst[i].cap > bif->lfcap)
bst[i].cap = bif->lfcap;
if (bst[i].cap > bix->lfcap)
bst[i].cap = bix->lfcap;
/* Calculate percent capacity remaining. */
bi[i].cap = (100 * bst[i].cap) / bif->lfcap;
bi[i].cap = (100 * bst[i].cap) / bix->lfcap;
/* If this battery is not present, don't use its capacity. */
if (bi[i].cap != -1) {
total_cap += bst[i].cap;
total_lfcap += bif->lfcap;
total_lfcap += bix->lfcap;
}
/*
@ -291,12 +291,9 @@ acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo)
error = 0;
out:
if (bi)
free(bi, M_TEMP);
if (bif)
free(bif, M_TEMP);
if (bst)
free(bst, M_TEMP);
free(bi, M_TEMP);
free(bix, M_TEMP);
free(bst, M_TEMP);
return (error);
}
@ -365,7 +362,8 @@ acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
unit = 0;
dev = NULL;
ioctl_arg = NULL;
if (IOCPARM_LEN(cmd) == sizeof(*ioctl_arg)) {
if (IOCPARM_LEN(cmd) == sizeof(union acpi_battery_ioctl_arg) ||
IOCPARM_LEN(cmd) == sizeof(union acpi_battery_ioctl_arg_v1)) {
ioctl_arg = (union acpi_battery_ioctl_arg *)addr;
unit = ioctl_arg->unit;
if (unit != ACPI_BATTERY_ALL_UNITS)
@ -376,12 +374,14 @@ acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
* No security check required: information retrieval only. If
* new functions are added here, a check might be required.
*/
/* Unit check */
switch (cmd) {
case ACPIIO_BATT_GET_UNITS:
*(int *)addr = acpi_battery_get_units();
error = 0;
break;
case ACPIIO_BATT_GET_BATTINFO:
case ACPIIO_BATT_GET_BATTINFO_V1:
if (dev != NULL || unit == ACPI_BATTERY_ALL_UNITS) {
bzero(&ioctl_arg->battinfo, sizeof(ioctl_arg->battinfo));
error = acpi_battery_get_battinfo(dev, &ioctl_arg->battinfo);
@ -390,24 +390,19 @@ acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
case ACPIIO_BATT_GET_BIF:
if (dev != NULL) {
bzero(&ioctl_arg->bif, sizeof(ioctl_arg->bif));
error = ACPI_BATT_GET_INFO(dev, &ioctl_arg->bif);
/*
* Remove invalid characters. Perhaps this should be done
* within a convenience function so all callers get the
* benefit.
*/
acpi_battery_clean_str(ioctl_arg->bif.model,
sizeof(ioctl_arg->bif.model));
acpi_battery_clean_str(ioctl_arg->bif.serial,
sizeof(ioctl_arg->bif.serial));
acpi_battery_clean_str(ioctl_arg->bif.type,
sizeof(ioctl_arg->bif.type));
acpi_battery_clean_str(ioctl_arg->bif.oeminfo,
sizeof(ioctl_arg->bif.oeminfo));
error = ACPI_BATT_GET_INFO(dev, &ioctl_arg->bif,
sizeof(ioctl_arg->bif));
}
break;
case ACPIIO_BATT_GET_BIX:
if (dev != NULL) {
bzero(&ioctl_arg->bix, sizeof(ioctl_arg->bix));
error = ACPI_BATT_GET_INFO(dev, &ioctl_arg->bix,
sizeof(ioctl_arg->bix));
}
break;
case ACPIIO_BATT_GET_BST:
case ACPIIO_BATT_GET_BST_V1:
if (dev != NULL) {
bzero(&ioctl_arg->bst, sizeof(ioctl_arg->bst));
error = ACPI_BATT_GET_STATUS(dev, &ioctl_arg->bst);
@ -417,6 +412,25 @@ acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg)
error = EINVAL;
}
/* Sanitize the string members. */
switch (cmd) {
case ACPIIO_BATT_GET_BIX:
case ACPIIO_BATT_GET_BIF:
/*
* Remove invalid characters. Perhaps this should be done
* within a convenience function so all callers get the
* benefit.
*/
acpi_battery_clean_str(ioctl_arg->bix.model,
sizeof(ioctl_arg->bix.model));
acpi_battery_clean_str(ioctl_arg->bix.serial,
sizeof(ioctl_arg->bix.serial));
acpi_battery_clean_str(ioctl_arg->bix.type,
sizeof(ioctl_arg->bix.type));
acpi_battery_clean_str(ioctl_arg->bix.oeminfo,
sizeof(ioctl_arg->bix.oeminfo));
};
return (error);
}
@ -450,26 +464,29 @@ acpi_battery_init(void)
ACPI_SERIAL_ASSERT(battery);
if (acpi_batteries_initialized)
return(0);
error = ENXIO;
dev = devclass_get_device(devclass_find("acpi"), 0);
if (dev == NULL)
goto out;
sc = device_get_softc(dev);
error = acpi_register_ioctl(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl,
NULL);
if (error != 0)
goto out;
error = acpi_register_ioctl(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl,
NULL);
if (error != 0)
goto out;
error = acpi_register_ioctl(ACPIIO_BATT_GET_BIF, acpi_battery_ioctl, NULL);
if (error != 0)
goto out;
error = acpi_register_ioctl(ACPIIO_BATT_GET_BST, acpi_battery_ioctl, NULL);
if (error != 0)
goto out;
#define ACPI_REGISTER_IOCTL(a, b, c) do { \
error = acpi_register_ioctl(a, b, c); \
if (error) \
goto out; \
} while (0)
ACPI_REGISTER_IOCTL(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl, NULL);
ACPI_REGISTER_IOCTL(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl, NULL);
ACPI_REGISTER_IOCTL(ACPIIO_BATT_GET_BATTINFO_V1, acpi_battery_ioctl, NULL);
ACPI_REGISTER_IOCTL(ACPIIO_BATT_GET_BIF, acpi_battery_ioctl, NULL);
ACPI_REGISTER_IOCTL(ACPIIO_BATT_GET_BIX, acpi_battery_ioctl, NULL);
ACPI_REGISTER_IOCTL(ACPIIO_BATT_GET_BST, acpi_battery_ioctl, NULL);
ACPI_REGISTER_IOCTL(ACPIIO_BATT_GET_BST_V1, acpi_battery_ioctl, NULL);
#undef ACPI_REGISTER_IOCTL
sysctl_ctx_init(&acpi_battery_sysctl_ctx);
acpi_battery_sysctl_tree = SYSCTL_ADD_NODE(&acpi_battery_sysctl_ctx,
@ -505,14 +522,17 @@ acpi_battery_init(void)
&acpi_battery_info_expire, 0,
"time in seconds until info is refreshed");
acpi_batteries_initted = TRUE;
acpi_batteries_initialized = TRUE;
out:
if (error != 0) {
if (error) {
acpi_deregister_ioctl(ACPIIO_BATT_GET_UNITS, acpi_battery_ioctl);
acpi_deregister_ioctl(ACPIIO_BATT_GET_BATTINFO, acpi_battery_ioctl);
acpi_deregister_ioctl(ACPIIO_BATT_GET_BATTINFO_V1, acpi_battery_ioctl);
acpi_deregister_ioctl(ACPIIO_BATT_GET_BIF, acpi_battery_ioctl);
acpi_deregister_ioctl(ACPIIO_BATT_GET_BIX, acpi_battery_ioctl);
acpi_deregister_ioctl(ACPIIO_BATT_GET_BST, acpi_battery_ioctl);
acpi_deregister_ioctl(ACPIIO_BATT_GET_BST_V1, acpi_battery_ioctl);
}
return (error);
}

View File

@ -61,12 +61,13 @@ ACPI_MODULE_NAME("BATTERY")
#define ACPI_BATTERY_BST_CHANGE 0x80
#define ACPI_BATTERY_BIF_CHANGE 0x81
#define ACPI_BATTERY_BIX_CHANGE ACPI_BATTERY_BIF_CHANGE
struct acpi_cmbat_softc {
device_t dev;
int flags;
struct acpi_bif bif;
struct acpi_bix bix;
struct acpi_bst bst;
struct timespec bst_lastupdated;
};
@ -82,10 +83,10 @@ static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify,
static int acpi_cmbat_info_expired(struct timespec *lastupdated);
static void acpi_cmbat_info_updated(struct timespec *lastupdated);
static void acpi_cmbat_get_bst(void *arg);
static void acpi_cmbat_get_bif_task(void *arg);
static void acpi_cmbat_get_bif(void *arg);
static int acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp);
static int acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp);
static void acpi_cmbat_get_bix_task(void *arg);
static void acpi_cmbat_get_bix(void *arg);
static int acpi_cmbat_bst(device_t, struct acpi_bst *);
static int acpi_cmbat_bix(device_t, void *, size_t);
static void acpi_cmbat_init_battery(void *arg);
static device_method_t acpi_cmbat_methods[] = {
@ -96,7 +97,7 @@ static device_method_t acpi_cmbat_methods[] = {
DEVMETHOD(device_resume, acpi_cmbat_resume),
/* ACPI battery interface */
DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif),
DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bix),
DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst),
DEVMETHOD_END
@ -205,12 +206,12 @@ acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
timespecclear(&sc->bst_lastupdated);
break;
case ACPI_NOTIFY_BUS_CHECK:
case ACPI_BATTERY_BIF_CHANGE:
case ACPI_BATTERY_BIX_CHANGE:
/*
* Queue a callback to get the current battery info from thread
* context. It's not safe to block in a notify handler.
*/
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bif_task, dev);
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bix_task, dev);
break;
}
@ -269,15 +270,15 @@ acpi_cmbat_get_bst(void *arg)
as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer);
if (ACPI_FAILURE(as)) {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"error fetching current battery status -- %s\n",
AcpiFormatException(as));
"error fetching current battery status -- %s\n",
AcpiFormatException(as));
goto end;
}
res = (ACPI_OBJECT *)bst_buffer.Pointer;
if (!ACPI_PKG_VALID(res, 4)) {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"battery status corrupted\n");
"battery status corrupted\n");
goto end;
}
@ -307,119 +308,194 @@ acpi_cmbat_get_bst(void *arg)
sc->flags &= ~ACPI_BATT_STAT_CRITICAL;
end:
if (bst_buffer.Pointer != NULL)
AcpiOsFree(bst_buffer.Pointer);
AcpiOsFree(bst_buffer.Pointer);
}
/* XXX There should be a cleaner way to do this locking. */
static void
acpi_cmbat_get_bif_task(void *arg)
acpi_cmbat_get_bix_task(void *arg)
{
ACPI_SERIAL_BEGIN(cmbat);
acpi_cmbat_get_bif(arg);
acpi_cmbat_get_bix(arg);
ACPI_SERIAL_END(cmbat);
}
static void
acpi_cmbat_get_bif(void *arg)
acpi_cmbat_get_bix(void *arg)
{
struct acpi_cmbat_softc *sc;
ACPI_STATUS as;
ACPI_OBJECT *res;
ACPI_HANDLE h;
ACPI_BUFFER bif_buffer;
ACPI_BUFFER bix_buffer;
device_t dev;
int i, n;
const struct {
enum { _BIX, _BIF } type;
char *name;
} bobjs[] = {
{ _BIX, "_BIX"},
{ _BIF, "_BIF"},
};
ACPI_SERIAL_ASSERT(cmbat);
dev = arg;
sc = device_get_softc(dev);
h = acpi_get_handle(dev);
bif_buffer.Pointer = NULL;
bif_buffer.Length = ACPI_ALLOCATE_BUFFER;
bix_buffer.Pointer = NULL;
bix_buffer.Length = ACPI_ALLOCATE_BUFFER;
as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer);
if (ACPI_FAILURE(as)) {
for (n = 0; n < sizeof(bobjs); n++) {
as = AcpiEvaluateObject(h, bobjs[n].name, NULL, &bix_buffer);
if (!ACPI_FAILURE(as)) {
res = (ACPI_OBJECT *)bix_buffer.Pointer;
break;
}
AcpiOsFree(bix_buffer.Pointer);
bix_buffer.Pointer = NULL;
bix_buffer.Length = ACPI_ALLOCATE_BUFFER;
}
/* Both _BIF and _BIX were not found. */
if (n == sizeof(bobjs)) {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"error fetching current battery info -- %s\n",
AcpiFormatException(as));
"error fetching current battery info -- %s\n",
AcpiFormatException(as));
goto end;
}
res = (ACPI_OBJECT *)bif_buffer.Pointer;
if (!ACPI_PKG_VALID(res, 13)) {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"battery info corrupted\n");
goto end;
/*
* ACPI _BIX and _BIF revision mismatch check:
*
* 1. _BIF has no revision field. The number of fields must be 13.
*
* 2. _BIX has a revision field. As of ACPI 6.3 it must be "0" or
* "1". The number of fields will be checked---20 and 21,
* respectively.
*
* If the revision number is grater than "1" and the number of
* fields is grater than 21, it will be treated as compatible with
* ACPI 6.0 _BIX. If not, it will be ignored.
*/
i = 0;
switch (bobjs[n].type) {
case _BIX:
if (acpi_PkgInt16(res, i++, &sc->bix.rev) != 0) {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"_BIX revision error\n");
goto end;
}
#define ACPI_BIX_REV_MISMATCH_ERR(x, r) do { \
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
"_BIX revision mismatch (%u != %u)\n", x, r); \
goto end; \
} while (0)
if (ACPI_PKG_VALID_EQ(res, 21)) { /* ACPI 6.0 _BIX */
if (sc->bix.rev != ACPI_BIX_REV_1)
ACPI_BIX_REV_MISMATCH_ERR(sc->bix.rev, ACPI_BIX_REV_1);
} else if (ACPI_PKG_VALID_EQ(res, 20)) {/* ACPI 4.0 _BIX */
if (sc->bix.rev != ACPI_BIX_REV_0)
ACPI_BIX_REV_MISMATCH_ERR(sc->bix.rev, ACPI_BIX_REV_0);
} else if (ACPI_PKG_VALID(res, 22) &&
ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_1 + 1)) {
/*
* Unknown _BIX with 22 or more members.
* Assume 21 members are compatible with 6.0 _BIX.
*/
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"Unknown _BIX revision(%u). "
"Assuming compatible with revision %u.\n",
sc->bix.rev, ACPI_BIX_REV_1);
} else {
/* Invalid _BIX. Ignore it. */
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"Invalid _BIX found (rev=%u, count=%u). Ignored.\n",
sc->bix.rev, res->Package.Count);
goto end;
}
break;
#undef ACPI_BIX_REV_MISMATCH_ERR
case _BIF:
if (ACPI_PKG_VALID_EQ(res, 13)) /* _BIF */
sc->bix.rev = ACPI_BIX_REV_BIF;
else {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"Invalid _BIF found (count=%u). Ignored.\n",
res->Package.Count);
goto end;
}
break;
}
if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0)
goto end;
if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0)
goto end;
if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0)
goto end;
if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0)
goto end;
if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0)
goto end;
if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0)
goto end;
if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0)
goto end;
if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0)
goto end;
if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0)
goto end;
if (acpi_PkgStr(res, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
if (acpi_PkgStr(res, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
if (acpi_PkgStr(res, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"rev = %04x\n", sc->bix.rev);
#define BIX_GETU32(NAME) do { \
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), \
#NAME " = %u\n", sc->bix.NAME); \
if (acpi_PkgInt32(res, i++, &sc->bix.NAME) != 0) \
goto end; \
} while (0)
BIX_GETU32(units);
BIX_GETU32(dcap);
BIX_GETU32(lfcap);
BIX_GETU32(btech);
BIX_GETU32(dvol);
BIX_GETU32(wcap);
BIX_GETU32(lcap);
if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_0)) {
BIX_GETU32(cycles);
BIX_GETU32(accuracy);
BIX_GETU32(stmax);
BIX_GETU32(stmin);
BIX_GETU32(aimax);
BIX_GETU32(aimin);
}
BIX_GETU32(gra1);
BIX_GETU32(gra2);
if (acpi_PkgStr(res, i++, sc->bix.model, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
if (acpi_PkgStr(res, i++, sc->bix.serial, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
if (acpi_PkgStr(res, i++, sc->bix.type, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
if (acpi_PkgStr(res, i++, sc->bix.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0)
goto end;
if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_1))
BIX_GETU32(scap);
#undef BIX_GETU32
end:
if (bif_buffer.Pointer != NULL)
AcpiOsFree(bif_buffer.Pointer);
AcpiOsFree(bix_buffer.Pointer);
}
static int
acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp)
acpi_cmbat_bix(device_t dev, void *bix, size_t len)
{
struct acpi_cmbat_softc *sc;
if (len != sizeof(struct acpi_bix) &&
len != sizeof(struct acpi_bif))
return (-1);
sc = device_get_softc(dev);
/*
* Just copy the data. The only value that should change is the
* last-full capacity, so we only update when we get a notify that says
* the info has changed. Many systems apparently take a long time to
* process a _BIF call so we avoid it if possible.
* process a _BI[FX] call so we avoid it if possible.
*/
ACPI_SERIAL_BEGIN(cmbat);
bifp->units = sc->bif.units;
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));
memcpy(bix, &sc->bix, len);
ACPI_SERIAL_END(cmbat);
return (0);
}
static int
acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp)
acpi_cmbat_bst(device_t dev, struct acpi_bst *bst)
{
struct acpi_cmbat_softc *sc;
@ -428,12 +504,9 @@ acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp)
ACPI_SERIAL_BEGIN(cmbat);
if (acpi_BatteryIsPresent(dev)) {
acpi_cmbat_get_bst(dev);
bstp->state = sc->bst.state;
bstp->rate = sc->bst.rate;
bstp->cap = sc->bst.cap;
bstp->volt = sc->bst.volt;
memcpy(bst, &sc->bst, sizeof(*bst));
} else
bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
bst->state = ACPI_BATT_STAT_NOT_PRESENT;
ACPI_SERIAL_END(cmbat);
return (0);
@ -448,7 +521,7 @@ acpi_cmbat_init_battery(void *arg)
dev = (device_t)arg;
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"battery initialization start\n");
"battery enitialization start\n");
/*
* Try repeatedly to get valid data from the battery. Since the
@ -487,11 +560,11 @@ acpi_cmbat_init_battery(void *arg)
timespecclear(&sc->bst_lastupdated);
acpi_cmbat_get_bst(dev);
}
if (retry == 0 || !acpi_battery_bif_valid(&sc->bif))
acpi_cmbat_get_bif(dev);
if (retry == 0 || !acpi_battery_bix_valid(&sc->bix))
acpi_cmbat_get_bix(dev);
valid = acpi_battery_bst_valid(&sc->bst) &&
acpi_battery_bif_valid(&sc->bif);
acpi_battery_bix_valid(&sc->bix);
ACPI_SERIAL_END(cmbat);
if (valid)
@ -500,9 +573,9 @@ acpi_cmbat_init_battery(void *arg)
if (retry == ACPI_CMBAT_RETRY_MAX) {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"battery initialization failed, giving up\n");
"battery initialization failed, giving up\n");
} else {
ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
"battery initialization done, tried %d times\n", retry + 1);
"battery initialization done, tried %d times\n", retry + 1);
}
}

View File

@ -53,6 +53,7 @@ HEADER {
typedef ACPI_STATUS (*acpi_scan_cb_t)(ACPI_HANDLE h, device_t *dev,
int level, void *arg);
struct acpi_bix;
struct acpi_bif;
struct acpi_bst;
};
@ -210,14 +211,16 @@ METHOD int ec_write {
};
#
# Get battery information (_BIF format)
# Get battery information (_BIF or _BIX format)
#
# device_t dev: Battery device
# struct acpi_bif *bif: Pointer to storage for _BIF results
# void *bix: Pointer to storage for _BIF or _BIX results
# size_t len: length of acpi_bif or acpi_bix.
#
METHOD int batt_get_info {
device_t dev;
struct acpi_bif *bif;
void *bix;
size_t len;
};
#

View File

@ -70,6 +70,19 @@ acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst)
return (error);
}
int
acpi_PkgInt16(ACPI_OBJECT *res, int idx, uint16_t *dst)
{
UINT64 tmp;
int error;
error = acpi_PkgInt(res, idx, &tmp);
if (error == 0)
*dst = (uint16_t)tmp;
return (error);
}
int
acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size)
{

View File

@ -46,9 +46,9 @@ struct acpi_smbat_softc {
uint8_t sb_base_addr;
device_t ec_dev;
struct acpi_bif bif;
struct acpi_bix bix;
struct acpi_bst bst;
struct timespec bif_lastupdated;
struct timespec bix_lastupdated;
struct timespec bst_lastupdated;
};
@ -57,7 +57,7 @@ static int acpi_smbat_attach(device_t dev);
static int acpi_smbat_shutdown(device_t dev);
static int acpi_smbat_info_expired(struct timespec *lastupdated);
static void acpi_smbat_info_updated(struct timespec *lastupdated);
static int acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif);
static int acpi_smbat_get_bix(device_t dev, void *, size_t);
static int acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst);
ACPI_SERIAL_DECL(smbat, "ACPI Smart Battery");
@ -87,7 +87,7 @@ static device_method_t acpi_smbat_methods[] = {
/* ACPI battery interface */
DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst),
DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif),
DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bix),
DEVMETHOD_END
};
@ -141,7 +141,7 @@ acpi_smbat_attach(device_t dev)
return (ENXIO);
}
timespecclear(&sc->bif_lastupdated);
timespecclear(&sc->bix_lastupdated);
timespecclear(&sc->bst_lastupdated);
if (acpi_battery_register(dev) != 0) {
@ -415,7 +415,7 @@ out:
}
static int
acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif)
acpi_smbat_get_bix(device_t dev, void *bix, size_t len)
{
struct acpi_smbat_softc *sc;
int error;
@ -423,70 +423,76 @@ acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif)
uint16_t val;
uint8_t addr;
if (len != sizeof(struct acpi_bix) &&
len != sizeof(struct acpi_bif))
return (-1);
ACPI_SERIAL_BEGIN(smbat);
addr = SMBATT_ADDRESS;
error = ENXIO;
sc = device_get_softc(dev);
if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) {
if (!acpi_smbat_info_expired(&sc->bix_lastupdated)) {
error = 0;
goto out;
}
sc->bix.rev = ACPI_BIX_REV_BIF;
if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
goto out;
if (val & SMBATT_BM_CAPACITY_MODE) {
factor = 10;
sc->bif.units = ACPI_BIF_UNITS_MW;
sc->bix.units = ACPI_BIX_UNITS_MW;
} else {
factor = 1;
sc->bif.units = ACPI_BIF_UNITS_MA;
sc->bix.units = ACPI_BIX_UNITS_MA;
}
if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val))
goto out;
sc->bif.dcap = val * factor;
sc->bix.dcap = val * factor;
if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val))
goto out;
sc->bif.lfcap = val * factor;
sc->bif.btech = 1; /* secondary (rechargeable) */
sc->bix.lfcap = val * factor;
sc->bix.btech = 1; /* secondary (rechargeable) */
if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val))
goto out;
sc->bif.dvol = val;
sc->bix.dvol = val;
sc->bif.wcap = sc->bif.dcap / 10;
sc->bif.lcap = sc->bif.dcap / 10;
sc->bix.wcap = sc->bix.dcap / 10;
sc->bix.lcap = sc->bix.dcap / 10;
sc->bif.gra1 = factor; /* not supported */
sc->bif.gra2 = factor; /* not supported */
sc->bix.gra1 = factor; /* not supported */
sc->bix.gra2 = factor; /* not supported */
if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME,
sc->bif.model, sizeof(sc->bif.model)))
sc->bix.model, sizeof(sc->bix.model)))
goto out;
if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val))
goto out;
snprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val);
snprintf(sc->bix.serial, sizeof(sc->bix.serial), "0x%04x", val);
if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY,
sc->bif.type, sizeof(sc->bif.type)))
sc->bix.type, sizeof(sc->bix.type)))
goto out;
if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA,
sc->bif.oeminfo, sizeof(sc->bif.oeminfo)))
sc->bix.oeminfo, sizeof(sc->bix.oeminfo)))
goto out;
/* XXX check if device was replugged during read? */
acpi_smbat_info_updated(&sc->bif_lastupdated);
acpi_smbat_info_updated(&sc->bix_lastupdated);
error = 0;
out:
if (error == 0)
memcpy(bif, &sc->bif, sizeof(sc->bif));
memcpy(bix, &sc->bix, len);
ACPI_SERIAL_END(smbat);
return (error);
}

View File

@ -48,9 +48,13 @@ struct acpi_battinfo {
int rate; /* emptying rate */
};
/*
* Battery Information object. Note that this object is deprecated in
* ACPI 4.0
*/
#define ACPI_CMBAT_MAXSTRLEN 32
struct acpi_bif {
uint32_t units; /* Units (mW or mA). */
uint32_t units; /* Power Unit (mW or mA). */
#define ACPI_BIF_UNITS_MW 0 /* Capacity in mWh, rate in mW. */
#define ACPI_BIF_UNITS_MA 1 /* Capacity in mAh, rate in mA. */
uint32_t dcap; /* Design Capacity */
@ -67,6 +71,76 @@ struct acpi_bif {
char oeminfo[ACPI_CMBAT_MAXSTRLEN]; /* OEM information */
};
/*
* Members in acpi_bix are reordered so that the first part is compatible
* with acpi_bif.
*/
struct acpi_bix {
/* _BIF-compatible */
uint32_t units; /* Power Unit (mW or mA). */
#define ACPI_BIX_UNITS_MW 0 /* Capacity in mWh, rate in mW. */
#define ACPI_BIX_UNITS_MA 1 /* Capacity in mAh, rate in mA. */
uint32_t dcap; /* Design Capacity */
uint32_t lfcap; /* Last Full capacity */
uint32_t btech; /* Battery Technology */
uint32_t dvol; /* Design voltage (mV) */
uint32_t wcap; /* WARN capacity */
uint32_t lcap; /* Low capacity */
uint32_t gra1; /* Granularity 1 (Warn to Low) */
uint32_t gra2; /* Granularity 2 (Full to Warn) */
char model[ACPI_CMBAT_MAXSTRLEN]; /* model identifier */
char serial[ACPI_CMBAT_MAXSTRLEN]; /* Serial number */
char type[ACPI_CMBAT_MAXSTRLEN]; /* Type */
char oeminfo[ACPI_CMBAT_MAXSTRLEN]; /* OEM information */
/* ACPI 4.0 or later */
uint16_t rev; /* Revision */
#define ACPI_BIX_REV_0 0 /* ACPI 4.0 _BIX */
#define ACPI_BIX_REV_1 1 /* ACPI 6.0 _BIX */
#define ACPI_BIX_REV_BIF 0xffff /* _BIF */
#define ACPI_BIX_REV_MIN_CHECK(x, min) \
(((min) == ACPI_BIX_REV_BIF) ? ((x) == ACPI_BIX_REV_BIF) : \
(((x) == ACPI_BIX_REV_BIF) ? 0 : ((x) >= (min))))
uint32_t cycles; /* Cycle Count */
uint32_t accuracy; /* Measurement Accuracy */
uint32_t stmax; /* Max Sampling Time */
uint32_t stmin; /* Min Sampling Time */
uint32_t aimax; /* Max Average Interval */
uint32_t aimin; /* Min Average Interval */
/* ACPI 6.0 or later */
uint32_t scap; /* Battery Swapping Capability */
#define ACPI_BIX_SCAP_NO 0x00000000
#define ACPI_BIX_SCAP_COLD 0x00000001
#define ACPI_BIX_SCAP_HOT 0x00000010
uint8_t bix_reserved[58]; /* padding */
};
#if 0
/* acpi_bix in the original order just for reference */
struct acpi_bix {
uint16_t rev; /* Revision */
uint32_t units; /* Power Unit (mW or mA). */
uint32_t dcap; /* Design Capacity */
uint32_t lfcap; /* Last Full capacity */
uint32_t btech; /* Battery Technology */
uint32_t dvol; /* Design voltage (mV) */
uint32_t wcap; /* Design Capacity of Warning */
uint32_t lcap; /* Design Capacity of Low */
uint32_t cycles; /* Cycle Count */
uint32_t accuracy; /* Measurement Accuracy */
uint32_t stmax; /* Max Sampling Time */
uint32_t stmin; /* Min Sampling Time */
uint32_t aimax; /* Max Average Interval */
uint32_t aimin; /* Min Average Interval */
uint32_t gra1; /* Granularity 1 (Warn to Low) */
uint32_t gra2; /* Granularity 2 (Full to Warn) */
char model[ACPI_CMBAT_MAXSTRLEN]; /* model identifier */
char serial[ACPI_CMBAT_MAXSTRLEN]; /* Serial number */
char type[ACPI_CMBAT_MAXSTRLEN]; /* Type */
char oeminfo[ACPI_CMBAT_MAXSTRLEN]; /* OEM information */
uint32_t scap; /* Battery Swapping Capability */
};
#endif
struct acpi_bst {
uint32_t state; /* Battery State */
uint32_t rate; /* Present Rate */
@ -91,7 +165,8 @@ struct acpi_bst {
(ACPI_BATT_STAT_INVALID | ACPI_BATT_STAT_CRITICAL)
#define ACPI_BATT_STAT_NOT_PRESENT ACPI_BATT_STAT_BST_MASK
union acpi_battery_ioctl_arg {
/* For backward compatibility */
union acpi_battery_ioctl_arg_v1 {
int unit; /* Device unit or ACPI_BATTERY_ALL_UNITS. */
struct acpi_battinfo battinfo;
@ -99,15 +174,27 @@ union acpi_battery_ioctl_arg {
struct acpi_bif bif;
struct acpi_bst bst;
};
union acpi_battery_ioctl_arg {
int unit; /* Device unit or ACPI_BATTERY_ALL_UNITS. */
struct acpi_battinfo battinfo;
struct acpi_bix bix;
struct acpi_bif bif;
struct acpi_bst bst;
};
#define ACPI_BATTERY_ALL_UNITS (-1)
#define ACPI_BATT_UNKNOWN 0xffffffff /* _BST or _BIF value unknown. */
#define ACPI_BATT_UNKNOWN 0xffffffff /* _BST or _BI[FX] value unknown. */
/* Common battery ioctls */
#define ACPIIO_BATT_GET_UNITS _IOR('B', 0x01, int)
#define ACPIIO_BATT_GET_BATTINFO _IOWR('B', 0x03, union acpi_battery_ioctl_arg)
#define ACPIIO_BATT_GET_BIF _IOWR('B', 0x10, union acpi_battery_ioctl_arg)
#define ACPIIO_BATT_GET_BATTINFO_V1 _IOWR('B', 0x03, union acpi_battery_ioctl_arg_v1)
#define ACPIIO_BATT_GET_BIF _IOWR('B', 0x10, union acpi_battery_ioctl_arg_v1)
#define ACPIIO_BATT_GET_BIX _IOWR('B', 0x10, union acpi_battery_ioctl_arg)
#define ACPIIO_BATT_GET_BST _IOWR('B', 0x11, union acpi_battery_ioctl_arg)
#define ACPIIO_BATT_GET_BST_V1 _IOWR('B', 0x11, union acpi_battery_ioctl_arg_v1)
/* Control Method battery ioctls (deprecated) */
#define ACPIIO_CMBAT_GET_BIF ACPIIO_BATT_GET_BIF

View File

@ -479,7 +479,7 @@ int acpi_battery_remove(device_t dev);
int acpi_battery_get_units(void);
int acpi_battery_get_info_expire(void);
int acpi_battery_bst_valid(struct acpi_bst *bst);
int acpi_battery_bif_valid(struct acpi_bif *bif);
int acpi_battery_bix_valid(struct acpi_bix *bix);
int acpi_battery_get_battinfo(device_t dev,
struct acpi_battinfo *info);
@ -493,8 +493,12 @@ int acpi_acad_get_acline(int *);
#define ACPI_PKG_VALID(pkg, size) \
((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE && \
(pkg)->Package.Count >= (size))
#define ACPI_PKG_VALID_EQ(pkg, size) \
((pkg) != NULL && (pkg)->Type == ACPI_TYPE_PACKAGE && \
(pkg)->Package.Count == (size))
int acpi_PkgInt(ACPI_OBJECT *res, int idx, UINT64 *dst);
int acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst);
int acpi_PkgInt16(ACPI_OBJECT *res, int idx, uint16_t *dst);
int acpi_PkgStr(ACPI_OBJECT *res, int idx, void *dst, size_t size);
int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *type,
int *rid, struct resource **dst, u_int flags);

View File

@ -96,34 +96,61 @@ acpi_battinfo(int num)
/* Print battery design information. */
battio.unit = num;
if (ioctl(acpifd, ACPIIO_BATT_GET_BIF, &battio) == -1)
if (ioctl(acpifd, ACPIIO_BATT_GET_BIX, &battio) == -1)
err(EX_IOERR, "get battery info (%d) failed", num);
amp = battio.bif.units;
amp = battio.bix.units;
pwr_units = amp ? "mA" : "mW";
if (battio.bif.dcap == UNKNOWN_CAP)
if (battio.bix.dcap == UNKNOWN_CAP)
printf("Design capacity:\tunknown\n");
else
printf("Design capacity:\t%d %sh\n", battio.bif.dcap,
printf("Design capacity:\t%d %sh\n", battio.bix.dcap,
pwr_units);
if (battio.bif.lfcap == UNKNOWN_CAP)
if (battio.bix.lfcap == UNKNOWN_CAP)
printf("Last full capacity:\tunknown\n");
else
printf("Last full capacity:\t%d %sh\n", battio.bif.lfcap,
printf("Last full capacity:\t%d %sh\n", battio.bix.lfcap,
pwr_units);
printf("Technology:\t\t%s\n", battio.bif.btech == 0 ?
printf("Technology:\t\t%s\n", battio.bix.btech == 0 ?
"primary (non-rechargeable)" : "secondary (rechargeable)");
if (battio.bif.dvol == UNKNOWN_CAP)
if (ACPI_BIX_REV_MIN_CHECK(battio.bix.rev, ACPI_BIX_REV_1)) {
printf("Battery Swappable Capability:\t");
if (battio.bix.scap == ACPI_BIX_SCAP_NO)
printf("Non-swappable\n");
else if (battio.bix.scap == ACPI_BIX_SCAP_COLD)
printf("cold swap\n");
else if (battio.bix.scap == ACPI_BIX_SCAP_HOT)
printf("hot swap\n");
else
printf("unknown\n");
}
if (battio.bix.dvol == UNKNOWN_CAP)
printf("Design voltage:\t\tunknown\n");
else
printf("Design voltage:\t\t%d mV\n", battio.bif.dvol);
printf("Capacity (warn):\t%d %sh\n", battio.bif.wcap, pwr_units);
printf("Capacity (low):\t\t%d %sh\n", battio.bif.lcap, pwr_units);
printf("Low/warn granularity:\t%d %sh\n", battio.bif.gra1, pwr_units);
printf("Warn/full granularity:\t%d %sh\n", battio.bif.gra2, pwr_units);
printf("Model number:\t\t%s\n", battio.bif.model);
printf("Serial number:\t\t%s\n", battio.bif.serial);
printf("Type:\t\t\t%s\n", battio.bif.type);
printf("OEM info:\t\t%s\n", battio.bif.oeminfo);
printf("Design voltage:\t\t%d mV\n", battio.bix.dvol);
printf("Capacity (warn):\t%d %sh\n", battio.bix.wcap, pwr_units);
printf("Capacity (low):\t\t%d %sh\n", battio.bix.lcap, pwr_units);
if (ACPI_BIX_REV_MIN_CHECK(battio.bix.rev, ACPI_BIX_REV_0)) {
if (battio.bix.cycles != ACPI_BATT_UNKNOWN)
printf("Cycle Count:\t\t%d\n", battio.bix.cycles);
printf("Mesurement Accuracy:\t%d %%\n",
battio.bix.accuracy / 1000);
if (battio.bix.stmax != ACPI_BATT_UNKNOWN)
printf("Max Sampling Time:\t%d ms\n",
battio.bix.stmax);
if (battio.bix.stmin != ACPI_BATT_UNKNOWN)
printf("Min Sampling Time:\t%d ms\n",
battio.bix.stmin);
printf("Max Average Interval:\t%d ms\n",
battio.bix.aimax);
printf("Min Average Interval:\t%d ms\n",
battio.bix.aimin);
}
printf("Low/warn granularity:\t%d %sh\n", battio.bix.gra1, pwr_units);
printf("Warn/full granularity:\t%d %sh\n", battio.bix.gra2, pwr_units);
printf("Model number:\t\t%s\n", battio.bix.model);
printf("Serial number:\t\t%s\n", battio.bix.serial);
printf("Type:\t\t\t%s\n", battio.bix.type);
printf("OEM info:\t\t%s\n", battio.bix.oeminfo);
/* Fetch battery voltage information. */
volt = UNKNOWN_VOLTAGE;