Index: head/share/man/man4/acpi_battery.4 =================================================================== --- head/share/man/man4/acpi_battery.4 (revision 358094) +++ head/share/man/man4/acpi_battery.4 (revision 358095) @@ -1,283 +1,380 @@ .\" .\" Copyright (c) 2019 Takanori Watanabe .\" 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$ .\" -.Dd November 26, 2019 +.Dd February 16, 2020 .Dt ACPI_BATTERY 4 .Os .Sh NAME .Nm acpi_battery .Nd ACPI battery management subsystem .Sh SYNOPSIS .Cd "device acpi" .Sh DESCRIPTION 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 .Pq ACPI Machine Language 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 interfaces as well as the .Xr devd 8 event notification interface. .Sh IOCTLS Every ioctl for the .Nm 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 .Pp 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 .Va warning . 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 .Va full . 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 object, which is the present battery status. In the case of a Smart Battery attached to SMBus, this ioctl will build a .Vt struct acpi_bst 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 .Sh SYSCTL VARIABLES The following .Xr sysctl 8 variables export battery status. Note that they are accumulated status of all of the connected batteries: .Bl -tag -width indent .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. .It Va hw.acpi.battery.units Number of battery units in the system. .It Va hw.acpi.battery.state Current battery charging status. This is same as .Va state of .Vt struct acpi_battinfo . .It Va hw.acpi.battery.rate Current battery discharging rate in mW. .It Va hw.acpi.battery.time Remaining battery life in minutes. If the battery is not discharging, the value shows .Li -1 . .It Va hw.acpi.battery.life Battery capacity in percent. .El .Sh EVENT NOTIFICATIONS Battery-related event notifications are sent to the userland via the .Xr devd 8 interface. See .Pa /etc/devd.conf and .Xr devd.conf 5 for more details. Note that notifications are supported only by the Control Method Battery. .Pp The .Nm driver sends events with the following attributes: .Pp .Bl -tag -width "subsystem" -compact .It system .Li ACPI .It subsystem .Li CMBAT .It type The fully qualified battery object path as in the ASL. .It notify An integer designating the event: .Pp .Bl -tag -width indent -compact .It Li 0x80 Battery status was changed. .It Li 0x81 Battery information was changed. .El .El .Sh SEE ALSO .Xr acpi 4 , .Xr acpiconf 8 .Sh AUTHORS .An -nosplit .An Nate Lawson Aq Mt njl@FreeBSD.org , .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 . Index: head/sys/dev/acpica/acpi_battery.c =================================================================== --- head/sys/dev/acpica/acpi_battery.c (revision 358094) +++ head/sys/dev/acpica/acpi_battery.c (revision 358095) @@ -1,518 +1,538 @@ /*- * Copyright (c) 2005 Nate Lawson * Copyright (c) 2000 Mitsuru IWASAKI * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_acpi.h" #include #include #include #include #include #include #include #include #include /* 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; static struct sysctl_oid *acpi_battery_sysctl_tree; ACPI_SERIAL_DECL(battery, "ACPI generic battery"); static void acpi_reset_battinfo(struct acpi_battinfo *info); static void acpi_battery_clean_str(char *str, int len); static device_t acpi_battery_find_dev(u_int logical_unit); static int acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg); static int acpi_battery_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_battery_units_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_battery_init(void); int 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); } int acpi_battery_remove(device_t dev) { return (0); } int acpi_battery_get_units(void) { devclass_t batt_dc; batt_dc = devclass_find("battery"); if (batt_dc == NULL) return (0); return (devclass_get_count(batt_dc)); } int acpi_battery_get_info_expire(void) { return (acpi_battery_info_expire); } /* Check _BST results for validity. */ int acpi_battery_bst_valid(struct acpi_bst *bst) { return (bst->state != ACPI_BATT_STAT_NOT_PRESENT && 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. */ int acpi_battery_get_battinfo(device_t dev, struct acpi_battinfo *battinfo) { int batt_stat, devcount, dev_idx, error, i; int total_cap, total_lfcap, total_min, valid_rate, valid_units; devclass_t batt_dc; device_t batt_dev; struct acpi_bst *bst; - struct acpi_bif *bif; + struct acpi_bix *bix; struct acpi_battinfo *bi; /* * Get the battery devclass and max unit for battery devices. If there * are none or error, return immediately. */ batt_dc = devclass_find("battery"); if (batt_dc == NULL) return (ENXIO); devcount = devclass_get_maxunit(batt_dc); if (devcount == 0) return (ENXIO); /* * 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, * calculate percent capacity remaining, and sum all the current * discharge rates. */ dev_idx = -1; batt_stat = valid_rate = valid_units = 0; total_cap = total_lfcap = 0; for (i = 0; i < devcount; i++) { /* Default info for every battery is "not present". */ acpi_reset_battinfo(&bi[i]); /* * Find the device. Since devcount is in terms of max units, this * may be a sparse array so skip devices that aren't present. */ batt_dev = devclass_get_device(batt_dc, i); if (batt_dev == NULL) continue; /* If examining a specific battery and this is it, record its index. */ if (dev != NULL && dev == batt_dev) dev_idx = i; /* * 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; /* * Record current state. If both charging and discharging are set, * ignore the charging flag. */ valid_units++; if ((bst[i].state & ACPI_BATT_STAT_DISCHARG) != 0) bst[i].state &= ~ACPI_BATT_STAT_CHARGING; batt_stat |= bst[i].state; bi[i].state = bst[i].state; /* * If the battery info is in terms of mA, convert to mW by * multiplying by the design voltage. If the design voltage * 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; /* * Some laptops report the "design-capacity" instead of the * "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; } /* * On systems with more than one battery, they may get used * sequentially, thus bst.rate may only signify the one currently * in use. For the remaining batteries, bst.rate will be zero, * which makes it impossible to calculate the total remaining time. * Therefore, we sum the bst.rate for batteries in the discharging * state and use the sum to calculate the total remaining time. */ if (bst[i].rate != ACPI_BATT_UNKNOWN && (bst[i].state & ACPI_BATT_STAT_DISCHARG) != 0) valid_rate += bst[i].rate; } /* If the caller asked for a device but we didn't find it, error. */ if (dev != NULL && dev_idx == -1) { error = ENXIO; goto out; } /* Pass 2: calculate capacity and remaining time for all batteries. */ total_min = 0; for (i = 0; i < devcount; i++) { /* * If any batteries are discharging, use the sum of the bst.rate * values. Otherwise, we are on AC power, and there is infinite * time remaining for this battery until we go offline. */ if (valid_rate > 0) bi[i].min = (60 * bst[i].cap) / valid_rate; else bi[i].min = 0; total_min += bi[i].min; } /* * Return total battery percent and time remaining. If there are * no valid batteries, report values as unknown. */ if (valid_units > 0) { if (dev == NULL) { battinfo->cap = (total_cap * 100) / total_lfcap; battinfo->min = total_min; battinfo->state = batt_stat; battinfo->rate = valid_rate; } else { battinfo->cap = bi[dev_idx].cap; battinfo->min = bi[dev_idx].min; battinfo->state = bi[dev_idx].state; battinfo->rate = bst[dev_idx].rate; } /* * If the queried battery has no discharge rate or is charging, * report that we don't know the remaining time. */ if (valid_rate == 0 || (battinfo->state & ACPI_BATT_STAT_CHARGING)) battinfo->min = -1; } else acpi_reset_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); } static void acpi_reset_battinfo(struct acpi_battinfo *info) { info->cap = -1; info->min = -1; info->state = ACPI_BATT_STAT_NOT_PRESENT; info->rate = -1; } /* Make string printable, removing invalid chars. */ static void acpi_battery_clean_str(char *str, int len) { int i; for (i = 0; i < len && *str != '\0'; i++, str++) { if (!isprint(*str)) *str = '?'; } /* NUL-terminate the string if we reached the end. */ if (i == len) *str = '\0'; } /* * The battery interface deals with devices and methods but userland * expects a logical unit number. Convert a logical unit to a device_t. */ static device_t acpi_battery_find_dev(u_int logical_unit) { int found_unit, i, maxunit; device_t dev; devclass_t batt_dc; dev = NULL; found_unit = 0; batt_dc = devclass_find("battery"); maxunit = devclass_get_maxunit(batt_dc); for (i = 0; i < maxunit; i++) { dev = devclass_get_device(batt_dc, i); if (dev == NULL) continue; if (logical_unit == found_unit) break; found_unit++; dev = NULL; } return (dev); } static int acpi_battery_ioctl(u_long cmd, caddr_t addr, void *arg) { union acpi_battery_ioctl_arg *ioctl_arg; int error, unit; device_t dev; /* For commands that use the ioctl_arg struct, validate it first. */ error = ENXIO; 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) dev = acpi_battery_find_dev(unit); } /* * 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); } break; 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); } break; default: 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); } static int acpi_battery_sysctl(SYSCTL_HANDLER_ARGS) { int val, error; acpi_battery_get_battinfo(NULL, &acpi_battery_battinfo); val = *(u_int *)oidp->oid_arg1; error = sysctl_handle_int(oidp, &val, 0, req); return (error); } static int acpi_battery_units_sysctl(SYSCTL_HANDLER_ARGS) { int count, error; count = acpi_battery_get_units(); error = sysctl_handle_int(oidp, &count, 0, req); return (error); } static int acpi_battery_init(void) { struct acpi_softc *sc; device_t dev; int error; 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, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "battery", CTLFLAG_RD, 0, "battery status and info"); SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx, SYSCTL_CHILDREN(acpi_battery_sysctl_tree), OID_AUTO, "life", CTLTYPE_INT | CTLFLAG_RD, &acpi_battery_battinfo.cap, 0, acpi_battery_sysctl, "I", "percent capacity remaining"); SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx, SYSCTL_CHILDREN(acpi_battery_sysctl_tree), OID_AUTO, "time", CTLTYPE_INT | CTLFLAG_RD, &acpi_battery_battinfo.min, 0, acpi_battery_sysctl, "I", "remaining time in minutes"); SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx, SYSCTL_CHILDREN(acpi_battery_sysctl_tree), OID_AUTO, "rate", CTLTYPE_INT | CTLFLAG_RD, &acpi_battery_battinfo.rate, 0, acpi_battery_sysctl, "I", "present rate in mW"); SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx, SYSCTL_CHILDREN(acpi_battery_sysctl_tree), OID_AUTO, "state", CTLTYPE_INT | CTLFLAG_RD, &acpi_battery_battinfo.state, 0, acpi_battery_sysctl, "I", "current status flags"); SYSCTL_ADD_PROC(&acpi_battery_sysctl_ctx, SYSCTL_CHILDREN(acpi_battery_sysctl_tree), OID_AUTO, "units", CTLTYPE_INT | CTLFLAG_RD, NULL, 0, acpi_battery_units_sysctl, "I", "number of batteries"); SYSCTL_ADD_INT(&acpi_battery_sysctl_ctx, SYSCTL_CHILDREN(acpi_battery_sysctl_tree), OID_AUTO, "info_expire", CTLFLAG_RW, &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); } Index: head/sys/dev/acpica/acpi_cmbat.c =================================================================== --- head/sys/dev/acpica/acpi_cmbat.c (revision 358094) +++ head/sys/dev/acpica/acpi_cmbat.c (revision 358095) @@ -1,508 +1,581 @@ /*- * Copyright (c) 2005 Nate Lawson * Copyright (c) 2000 Munehiro Matsuda * Copyright (c) 2000 Takanori Watanabe * Copyright (c) 2000 Mitsuru IWASAKI * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_acpi.h" #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data"); /* Number of times to retry initialization before giving up. */ #define ACPI_CMBAT_RETRY_MAX 6 /* Check the battery once a minute. */ #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 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; }; ACPI_SERIAL_DECL(cmbat, "ACPI cmbat"); static int acpi_cmbat_probe(device_t dev); static int acpi_cmbat_attach(device_t dev); static int acpi_cmbat_detach(device_t dev); static int acpi_cmbat_resume(device_t dev); static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context); 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[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_cmbat_probe), DEVMETHOD(device_attach, acpi_cmbat_attach), DEVMETHOD(device_detach, acpi_cmbat_detach), 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 }; static driver_t acpi_cmbat_driver = { "battery", 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); MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1); static int acpi_cmbat_probe(device_t dev) { static char *cmbat_ids[] = { "PNP0C0A", NULL }; int rv; if (acpi_disabled("cmbat")) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids, NULL); if (rv <= 0) device_set_desc(dev, "ACPI Control Method Battery"); return (rv); } static int acpi_cmbat_attach(device_t dev) { int error; ACPI_HANDLE handle; struct acpi_cmbat_softc *sc; sc = device_get_softc(dev); handle = acpi_get_handle(dev); sc->dev = dev; timespecclear(&sc->bst_lastupdated); error = acpi_battery_register(dev); if (error != 0) { device_printf(dev, "registering battery failed\n"); return (error); } /* * Install a system notify handler in addition to the device notify. * Toshiba notebook uses this alternate notify for its battery. */ AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler, dev); AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev); return (0); } static int acpi_cmbat_detach(device_t dev) { ACPI_HANDLE handle; handle = acpi_get_handle(dev); AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler); acpi_battery_remove(dev); /* * Force any pending notification handler calls to complete by * requesting cmbat serialisation while freeing and clearing the * softc pointer: */ ACPI_SERIAL_BEGIN(cmbat); device_set_softc(dev, NULL); ACPI_SERIAL_END(cmbat); return (0); } static int acpi_cmbat_resume(device_t dev) { AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev); return (0); } static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) { struct acpi_cmbat_softc *sc; device_t dev; dev = (device_t)context; sc = device_get_softc(dev); switch (notify) { case ACPI_NOTIFY_DEVICE_CHECK: case ACPI_BATTERY_BST_CHANGE: /* * Clear the last updated time. The next call to retrieve the * battery status will get the new value for us. */ 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; } acpi_UserNotify("CMBAT", h, notify); } static int acpi_cmbat_info_expired(struct timespec *lastupdated) { struct timespec curtime; ACPI_SERIAL_ASSERT(cmbat); if (lastupdated == NULL) return (TRUE); if (!timespecisset(lastupdated)) return (TRUE); getnanotime(&curtime); timespecsub(&curtime, lastupdated, &curtime); return (curtime.tv_sec < 0 || curtime.tv_sec > acpi_battery_get_info_expire()); } static void acpi_cmbat_info_updated(struct timespec *lastupdated) { ACPI_SERIAL_ASSERT(cmbat); if (lastupdated != NULL) getnanotime(lastupdated); } static void acpi_cmbat_get_bst(void *arg) { struct acpi_cmbat_softc *sc; ACPI_STATUS as; ACPI_OBJECT *res; ACPI_HANDLE h; ACPI_BUFFER bst_buffer; device_t dev; ACPI_SERIAL_ASSERT(cmbat); dev = arg; sc = device_get_softc(dev); h = acpi_get_handle(dev); bst_buffer.Pointer = NULL; bst_buffer.Length = ACPI_ALLOCATE_BUFFER; if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) goto end; 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; } if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0) goto end; if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0) goto end; if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0) goto end; if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0) goto end; acpi_cmbat_info_updated(&sc->bst_lastupdated); /* Clear out undefined/extended bits that might be set by hardware. */ sc->bst.state &= ACPI_BATT_STAT_BST_MASK; if ((sc->bst.state & ACPI_BATT_STAT_INVALID) == ACPI_BATT_STAT_INVALID) ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), "battery reports simultaneous charging and discharging\n"); /* XXX If all batteries are critical, perhaps we should suspend. */ if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) { if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) { sc->flags |= ACPI_BATT_STAT_CRITICAL; device_printf(dev, "critically low charge!\n"); } } else 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; sc = device_get_softc(dev); 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); } static void acpi_cmbat_init_battery(void *arg) { struct acpi_cmbat_softc *sc; int retry, valid; device_t dev; 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 * embedded controller isn't always ready just after boot, we may have * to wait a while. */ for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) { /* * Batteries on DOCK can be ejected w/ DOCK during retrying. * * If there is a valid softc pointer the device may be in * attaching, attached or detaching state. If the state is * different from attached retry getting the device state * until it becomes stable. This solves a race if the ACPI * notification handler is called during attach, because * device_is_attached() doesn't return non-zero until after * the attach code has been executed. */ ACPI_SERIAL_BEGIN(cmbat); sc = device_get_softc(dev); if (sc == NULL) { ACPI_SERIAL_END(cmbat); return; } if (!acpi_BatteryIsPresent(dev) || !device_is_attached(dev)) { ACPI_SERIAL_END(cmbat); continue; } /* * Only query the battery if this is the first try or the specific * type of info is still invalid. */ if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) { 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) break; } 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); } } Index: head/sys/dev/acpica/acpi_if.m =================================================================== --- head/sys/dev/acpica/acpi_if.m (revision 358094) +++ head/sys/dev/acpica/acpi_if.m (revision 358095) @@ -1,232 +1,235 @@ #- # Copyright (c) 2004 Nate Lawson # 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 #include #include INTERFACE acpi; # # Callback function for each child handle traversed in acpi_scan_children(). # # ACPI_HANDLE h: current child device being considered # # device_t *dev: pointer to the child's original device_t or NULL if there # was none. The callback should store a new device in *dev if it has # created one. The method implementation will automatically clean up the # previous device and properly associate the current ACPI_HANDLE with it. # # level: current level being scanned # # void *arg: argument passed in original call to acpi_scan_children() # # Returns: AE_OK if the scan should continue, otherwise an error # 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; }; # # Default implementation for acpi_id_probe(). # CODE { static int acpi_generic_id_probe(device_t bus, device_t dev, char **ids, char **match) { return (ENXIO); } }; # # Check a device for a match in a list of ID strings. The strings can be # EISA PNP IDs or ACPI _HID/_CID values. # # device_t bus: parent bus for the device # # device_t dev: device being considered # # char **ids: array of ID strings to consider # # char **match: Pointer to store ID string matched or NULL if no match # pass NULL if not needed. # # Returns: BUS_PROBE_DEFAULT if _HID match # BUS_PROBE_LOW_PRIORITY if _CID match and not _HID match # ENXIO if no match. # METHOD int id_probe { device_t bus; device_t dev; char **ids; char **match; } DEFAULT acpi_generic_id_probe; # # Evaluate an ACPI method or object, given its path. # # device_t bus: parent bus for the device # # device_t dev: evaluate the object relative to this device's handle. # Specify NULL to begin the search at the ACPI root. # # ACPI_STRING pathname: absolute or relative path to this object # # ACPI_OBJECT_LIST *parameters: array of arguments to pass to the object. # Specify NULL if there are none. # # ACPI_BUFFER *ret: the result (if any) of the evaluation # Specify NULL if there is none. # # Returns: AE_OK or an error value # METHOD ACPI_STATUS evaluate_object { device_t bus; device_t dev; ACPI_STRING pathname; ACPI_OBJECT_LIST *parameters; ACPI_BUFFER *ret; }; # # Get the highest power state (D0-D3) that is usable for a device when # suspending/resuming. If a bus calls this when suspending a device, it # must also call it when resuming. # # device_t bus: parent bus for the device # # device_t dev: check this device's appropriate power state # # int *dstate: if successful, contains the highest valid sleep state # # Returns: 0 on success or some other error value. # METHOD int pwr_for_sleep { device_t bus; device_t dev; int *dstate; }; # # Rescan a subtree and optionally reattach devices to handles. Users # specify a callback that is called for each ACPI_HANDLE of type Device # that is a child of "dev". # # device_t bus: parent bus for the device # # device_t dev: begin the scan starting with this device's handle. # Specify NULL to begin the scan at the ACPI root. # # int max_depth: number of levels to traverse (i.e., 1 means just the # immediate children. # # acpi_scan_cb_t user_fn: called for each child handle # # void *arg: argument to pass to the callback function # # Returns: AE_OK or an error value, based on the callback return value # METHOD ACPI_STATUS scan_children { device_t bus; device_t dev; int max_depth; acpi_scan_cb_t user_fn; void *arg; }; # # Query a given driver for its supported feature(s). This should be # called by the parent bus before the driver is probed. # # driver_t *driver: child driver # # u_int *features: returned bitmask of all supported features # STATICMETHOD int get_features { driver_t *driver; u_int *features; }; # # Read embedded controller (EC) address space # # device_t dev: EC device # u_int addr: Address to read from in EC space # UINT64 *val: Location to store read value # int width: Size of area to read in bytes # METHOD int ec_read { device_t dev; u_int addr; UINT64 *val; int width; }; # # Write embedded controller (EC) address space # # device_t dev: EC device # u_int addr: Address to write to in EC space # UINT64 val: Value to write # int width: Size of value to write in bytes # METHOD int ec_write { device_t dev; u_int addr; UINT64 val; int width; }; # -# 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; }; # # Get battery status (_BST format) # # device_t dev: Battery device # struct acpi_bst *bst: Pointer to storage for _BST results # METHOD int batt_get_status { device_t dev; struct acpi_bst *bst; }; Index: head/sys/dev/acpica/acpi_package.c =================================================================== --- head/sys/dev/acpica/acpi_package.c (revision 358094) +++ head/sys/dev/acpica/acpi_package.c (revision 358095) @@ -1,173 +1,186 @@ /*- * Copyright (c) 2003 Nate Lawson * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include /* * Package manipulation convenience functions */ int acpi_PkgInt(ACPI_OBJECT *res, int idx, UINT64 *dst) { ACPI_OBJECT *obj; obj = &res->Package.Elements[idx]; if (obj->Type != ACPI_TYPE_INTEGER) return (EINVAL); *dst = obj->Integer.Value; return (0); } int acpi_PkgInt32(ACPI_OBJECT *res, int idx, uint32_t *dst) { UINT64 tmp; int error; error = acpi_PkgInt(res, idx, &tmp); if (error == 0) *dst = (uint32_t)tmp; 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) { ACPI_OBJECT *obj; void *ptr; size_t length; obj = &res->Package.Elements[idx]; if (obj == NULL) return (EINVAL); switch (obj->Type) { case ACPI_TYPE_STRING: ptr = obj->String.Pointer; length = obj->String.Length; break; case ACPI_TYPE_BUFFER: ptr = obj->Buffer.Pointer; length = obj->Buffer.Length; break; default: return (EINVAL); } /* Make sure string will fit, including terminating NUL */ if (++length > size) return (E2BIG); strlcpy(dst, ptr, length); return (0); } int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *type, int *rid, struct resource **dst, u_int flags) { ACPI_GENERIC_ADDRESS gas; ACPI_OBJECT *obj; obj = &res->Package.Elements[idx]; if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER || obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) return (EINVAL); memcpy(&gas, obj->Buffer.Pointer + 3, sizeof(gas)); return (acpi_bus_alloc_gas(dev, type, rid, &gas, dst, flags)); } int acpi_PkgFFH_IntelCpu(ACPI_OBJECT *res, int idx, int *vendor, int *class, uint64_t *address, int *accsize) { ACPI_GENERIC_ADDRESS gas; ACPI_OBJECT *obj; obj = &res->Package.Elements[idx]; if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER || obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) return (EINVAL); memcpy(&gas, obj->Buffer.Pointer + 3, sizeof(gas)); if (gas.SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) return (ERESTART); *vendor = gas.BitWidth; *class = gas.BitOffset; *address = gas.Address; *accsize = gas.AccessWidth; return (0); } ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj) { ACPI_HANDLE h; if (obj == NULL) return (NULL); switch (obj->Type) { case ACPI_TYPE_LOCAL_REFERENCE: case ACPI_TYPE_ANY: h = obj->Reference.Handle; break; case ACPI_TYPE_STRING: /* * The String object usually contains a fully-qualified path, so * scope can be NULL. * * XXX This may not always be the case. */ if (ACPI_FAILURE(AcpiGetHandle(scope, obj->String.Pointer, &h))) h = NULL; break; default: h = NULL; break; } return (h); } Index: head/sys/dev/acpica/acpi_smbat.c =================================================================== --- head/sys/dev/acpica/acpi_smbat.c (revision 358094) +++ head/sys/dev/acpica/acpi_smbat.c (revision 358095) @@ -1,492 +1,498 @@ /*- * Copyright (c) 2005 Hans Petter Selasky * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_acpi.h" #include #include #include #include #include #include #include #include /* Transactions have failed after 500 ms. */ #define SMBUS_TIMEOUT 50 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; }; static int acpi_smbat_probe(device_t dev); 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"); static SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, CTLFLAG_RD, NULL, "Battery debugging"); /* On some laptops with smart batteries, enabling battery monitoring * software causes keystrokes from atkbd to be lost. This has also been * reported on Linux, and is apparently due to the keyboard and I2C line * for the battery being routed through the same chip. Whether that's * accurate or not, adding extra sleeps to the status checking code * causes the problem to go away. * * If you experience that problem, try a value of 10ms and move up * from there. */ static int batt_sleep_ms; SYSCTL_INT(_debug_acpi_batt, OID_AUTO, batt_sleep_ms, CTLFLAG_RW, &batt_sleep_ms, 0, "Sleep during battery status updates to prevent keystroke loss."); static device_method_t acpi_smbat_methods[] = { /* device interface */ DEVMETHOD(device_probe, acpi_smbat_probe), DEVMETHOD(device_attach, acpi_smbat_attach), DEVMETHOD(device_shutdown, acpi_smbat_shutdown), /* 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 }; static driver_t acpi_smbat_driver = { "battery", acpi_smbat_methods, sizeof(struct acpi_smbat_softc), }; static devclass_t acpi_smbat_devclass; DRIVER_MODULE(acpi_smbat, acpi, acpi_smbat_driver, acpi_smbat_devclass, 0, 0); MODULE_DEPEND(acpi_smbat, acpi, 1, 1, 1); static int acpi_smbat_probe(device_t dev) { static char *smbat_ids[] = {"ACPI0001", "ACPI0005", NULL}; ACPI_STATUS status; int rv; if (acpi_disabled("smbat")) return (ENXIO); rv = ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids, NULL); if (rv > 0) return (rv); status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL); if (ACPI_FAILURE(status)) return (ENXIO); device_set_desc(dev, "ACPI Smart Battery"); return (rv); } static int acpi_smbat_attach(device_t dev) { struct acpi_smbat_softc *sc; uint32_t base; sc = device_get_softc(dev); if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) { device_printf(dev, "cannot get EC base address\n"); return (ENXIO); } sc->sb_base_addr = (base >> 8) & 0xff; /* XXX Only works with one EC, but nearly all systems only have one. */ sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0); if (sc->ec_dev == NULL) { device_printf(dev, "cannot find EC device\n"); return (ENXIO); } - timespecclear(&sc->bif_lastupdated); + timespecclear(&sc->bix_lastupdated); timespecclear(&sc->bst_lastupdated); if (acpi_battery_register(dev) != 0) { device_printf(dev, "cannot register battery\n"); return (ENXIO); } return (0); } static int acpi_smbat_shutdown(device_t dev) { acpi_battery_remove(dev); return (0); } static int acpi_smbat_info_expired(struct timespec *lastupdated) { struct timespec curtime; ACPI_SERIAL_ASSERT(smbat); if (lastupdated == NULL) return (TRUE); if (!timespecisset(lastupdated)) return (TRUE); getnanotime(&curtime); timespecsub(&curtime, lastupdated, &curtime); return (curtime.tv_sec < 0 || curtime.tv_sec > acpi_battery_get_info_expire()); } static void acpi_smbat_info_updated(struct timespec *lastupdated) { ACPI_SERIAL_ASSERT(smbat); if (lastupdated != NULL) getnanotime(lastupdated); } static int acpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, uint16_t *ptr) { int error, to; UINT64 val; ACPI_SERIAL_ASSERT(smbat); if (batt_sleep_ms) AcpiOsSleep(batt_sleep_ms); val = addr; error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, val, 1); if (error) goto out; val = cmd; error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, val, 1); if (error) goto out; val = 0x09; /* | 0x80 if PEC */ error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, val, 1); if (error) goto out; if (batt_sleep_ms) AcpiOsSleep(batt_sleep_ms); for (to = SMBUS_TIMEOUT; to != 0; to--) { error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, &val, 1); if (error) goto out; if (val == 0) break; AcpiOsSleep(10); } if (to == 0) { error = ETIMEDOUT; goto out; } error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); if (error) goto out; if (val & SMBUS_STS_MASK) { printf("%s: AE_ERROR 0x%x\n", __FUNCTION__, (int)(val & SMBUS_STS_MASK)); error = EIO; goto out; } error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA, &val, 2); if (error) goto out; *ptr = val; out: return (error); } static int acpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, uint8_t *ptr, uint16_t len) { UINT64 val; uint8_t to; int error; ACPI_SERIAL_ASSERT(smbat); if (batt_sleep_ms) AcpiOsSleep(batt_sleep_ms); val = addr; error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, val, 1); if (error) goto out; val = cmd; error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, val, 1); if (error) goto out; val = 0x0B /* | 0x80 if PEC */ ; error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, val, 1); if (error) goto out; if (batt_sleep_ms) AcpiOsSleep(batt_sleep_ms); for (to = SMBUS_TIMEOUT; to != 0; to--) { error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, &val, 1); if (error) goto out; if (val == 0) break; AcpiOsSleep(10); } if (to == 0) { error = ETIMEDOUT; goto out; } error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); if (error) goto out; if (val & SMBUS_STS_MASK) { printf("%s: AE_ERROR 0x%x\n", __FUNCTION__, (int)(val & SMBUS_STS_MASK)); error = EIO; goto out; } /* get length */ error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT, &val, 1); if (error) goto out; val = (val & 0x1f) + 1; bzero(ptr, len); if (len > val) len = val; if (batt_sleep_ms) AcpiOsSleep(batt_sleep_ms); while (len--) { error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA + len, &val, 1); if (error) goto out; ptr[len] = val; if (batt_sleep_ms) AcpiOsSleep(batt_sleep_ms); } out: return (error); } static int acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst) { struct acpi_smbat_softc *sc; int error; uint32_t factor; int16_t val; uint8_t addr; ACPI_SERIAL_BEGIN(smbat); addr = SMBATT_ADDRESS; error = ENXIO; sc = device_get_softc(dev); if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) { error = 0; goto out; } if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) goto out; if (val & SMBATT_BM_CAPACITY_MODE) factor = 10; else factor = 1; /* get battery status */ if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val)) goto out; sc->bst.state = 0; if (val & SMBATT_BS_DISCHARGING) sc->bst.state |= ACPI_BATT_STAT_DISCHARG; if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM) sc->bst.state |= ACPI_BATT_STAT_CRITICAL; /* * If the rate is negative, it is discharging. Otherwise, * it is charging. */ if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val)) goto out; if (val > 0) { sc->bst.rate = val * factor; sc->bst.state &= ~SMBATT_BS_DISCHARGING; sc->bst.state |= ACPI_BATT_STAT_CHARGING; } else if (val < 0) sc->bst.rate = (-val) * factor; else sc->bst.rate = 0; if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val)) goto out; sc->bst.cap = val * factor; if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val)) goto out; sc->bst.volt = val; acpi_smbat_info_updated(&sc->bst_lastupdated); error = 0; out: if (error == 0) memcpy(bst, &sc->bst, sizeof(sc->bst)); ACPI_SERIAL_END(smbat); return (error); } 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; uint32_t factor; 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); } Index: head/sys/dev/acpica/acpiio.h =================================================================== --- head/sys/dev/acpica/acpiio.h (revision 358094) +++ head/sys/dev/acpica/acpiio.h (revision 358095) @@ -1,125 +1,212 @@ /*- * Copyright (c) 1999 Takanori Watanabe * Copyright (c) 1999 Mitsuru IWASAKI * 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$ */ #ifndef _ACPIIO_H_ #define _ACPIIO_H_ /* * Core ACPI subsystem ioctls */ #define ACPIIO_SETSLPSTATE _IOW('P', 3, int) /* DEPRECATED */ /* Request S1-5 sleep state. User is notified and then sleep proceeds. */ #define ACPIIO_REQSLPSTATE _IOW('P', 4, int) /* Allow suspend to continue (0) or abort it (errno). */ #define ACPIIO_ACKSLPSTATE _IOW('P', 5, int) struct acpi_battinfo { int cap; /* percent */ int min; /* remaining time (in minutes) */ int state; /* battery state */ 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 */ 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 */ }; +/* + * 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 */ uint32_t cap; /* Remaining Capacity */ uint32_t volt; /* Present Voltage */ }; /* * Note that the following definitions represent status bits for internal * driver state. The first three of them (charging, discharging and critical) * conveninetly conform to ACPI specification of status returned by _BST * method. Other definitions (not present, etc) are synthetic. * Also note that according to the specification the charging and discharging * status bits must not be set at the same time. */ #define ACPI_BATT_STAT_DISCHARG 0x0001 #define ACPI_BATT_STAT_CHARGING 0x0002 #define ACPI_BATT_STAT_CRITICAL 0x0004 #define ACPI_BATT_STAT_INVALID \ (ACPI_BATT_STAT_DISCHARG | ACPI_BATT_STAT_CHARGING) #define ACPI_BATT_STAT_BST_MASK \ (ACPI_BATT_STAT_INVALID | ACPI_BATT_STAT_CRITICAL) #define ACPI_BATT_STAT_NOT_PRESENT ACPI_BATT_STAT_BST_MASK +/* For backward compatibility */ +union acpi_battery_ioctl_arg_v1 { + int unit; /* Device unit or ACPI_BATTERY_ALL_UNITS. */ + + struct acpi_battinfo battinfo; + + 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 #define ACPIIO_CMBAT_GET_BST ACPIIO_BATT_GET_BST /* Get AC adapter status. */ #define ACPIIO_ACAD_GET_STATUS _IOR('A', 1, int) #ifdef _KERNEL typedef int (*acpi_ioctl_fn)(u_long cmd, caddr_t addr, void *arg); extern int acpi_register_ioctl(u_long cmd, acpi_ioctl_fn fn, void *arg); extern void acpi_deregister_ioctl(u_long cmd, acpi_ioctl_fn fn); #endif #endif /* !_ACPIIO_H_ */ Index: head/sys/dev/acpica/acpivar.h =================================================================== --- head/sys/dev/acpica/acpivar.h (revision 358094) +++ head/sys/dev/acpica/acpivar.h (revision 358095) @@ -1,556 +1,560 @@ /*- * Copyright (c) 2000 Mitsuru IWASAKI * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * 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$ */ #ifndef _ACPIVAR_H_ #define _ACPIVAR_H_ #ifdef _KERNEL #include "acpi_if.h" #include "bus_if.h" #include #ifdef INTRNG #include #endif #include #include #include #include #include #include #include #include struct apm_clone_data; struct acpi_softc { device_t acpi_dev; struct cdev *acpi_dev_t; int acpi_enabled; int acpi_sstate; int acpi_sleep_disabled; int acpi_resources_reserved; struct sysctl_ctx_list acpi_sysctl_ctx; struct sysctl_oid *acpi_sysctl_tree; int acpi_power_button_sx; int acpi_sleep_button_sx; int acpi_lid_switch_sx; int acpi_standby_sx; int acpi_suspend_sx; int acpi_sleep_delay; int acpi_s4bios; int acpi_do_disable; int acpi_verbose; int acpi_handle_reboot; vm_offset_t acpi_wakeaddr; vm_paddr_t acpi_wakephys; int acpi_next_sstate; /* Next suspend Sx state. */ struct apm_clone_data *acpi_clone; /* Pseudo-dev for devd(8). */ STAILQ_HEAD(,apm_clone_data) apm_cdevs; /* All apm/apmctl/acpi cdevs. */ struct callout susp_force_to; /* Force suspend if no acks. */ }; struct acpi_device { /* ACPI ivars */ ACPI_HANDLE ad_handle; void *ad_private; int ad_flags; int ad_cls_class; /* Resources */ struct resource_list ad_rl; }; #ifdef INTRNG struct intr_map_data_acpi { struct intr_map_data hdr; u_int irq; u_int pol; u_int trig; }; #endif /* Track device (/dev/{apm,apmctl} and /dev/acpi) notification status. */ struct apm_clone_data { STAILQ_ENTRY(apm_clone_data) entries; struct cdev *cdev; int flags; #define ACPI_EVF_NONE 0 /* /dev/apm semantics */ #define ACPI_EVF_DEVD 1 /* /dev/acpi is handled via devd(8) */ #define ACPI_EVF_WRITE 2 /* Device instance is opened writable. */ int notify_status; #define APM_EV_NONE 0 /* Device not yet aware of pending sleep. */ #define APM_EV_NOTIFIED 1 /* Device saw next sleep state. */ #define APM_EV_ACKED 2 /* Device agreed sleep can occur. */ struct acpi_softc *acpi_sc; struct selinfo sel_read; }; #define ACPI_PRW_MAX_POWERRES 8 struct acpi_prw_data { ACPI_HANDLE gpe_handle; int gpe_bit; int lowest_wake; ACPI_OBJECT power_res[ACPI_PRW_MAX_POWERRES]; int power_res_count; }; /* Flags for each device defined in the AML namespace. */ #define ACPI_FLAG_WAKE_ENABLED 0x1 /* Macros for extracting parts of a PCI address from an _ADR value. */ #define ACPI_ADR_PCI_SLOT(adr) (((adr) & 0xffff0000) >> 16) #define ACPI_ADR_PCI_FUNC(adr) ((adr) & 0xffff) /* * Entry points to ACPI from above are global functions defined in this * file, sysctls, and I/O on the control device. Entry points from below * are interrupts (the SCI), notifies, task queue threads, and the thermal * zone polling thread. * * ACPI tables and global shared data are protected by a global lock * (acpi_mutex). * * Each ACPI device can have its own driver-specific mutex for protecting * shared access to local data. The ACPI_LOCK macros handle mutexes. * * Drivers that need to serialize access to functions (e.g., to route * interrupts, get/set control paths, etc.) should use the sx lock macros * (ACPI_SERIAL). * * ACPI-CA handles its own locking and should not be called with locks held. * * The most complicated path is: * GPE -> EC runs _Qxx -> _Qxx reads EC space -> GPE */ extern struct mtx acpi_mutex; #define ACPI_LOCK(sys) mtx_lock(&sys##_mutex) #define ACPI_UNLOCK(sys) mtx_unlock(&sys##_mutex) #define ACPI_LOCK_ASSERT(sys) mtx_assert(&sys##_mutex, MA_OWNED); #define ACPI_LOCK_DECL(sys, name) \ static struct mtx sys##_mutex; \ MTX_SYSINIT(sys##_mutex, &sys##_mutex, name, MTX_DEF) #define ACPI_SERIAL_BEGIN(sys) sx_xlock(&sys##_sxlock) #define ACPI_SERIAL_END(sys) sx_xunlock(&sys##_sxlock) #define ACPI_SERIAL_ASSERT(sys) sx_assert(&sys##_sxlock, SX_XLOCKED); #define ACPI_SERIAL_DECL(sys, name) \ static struct sx sys##_sxlock; \ SX_SYSINIT(sys##_sxlock, &sys##_sxlock, name) /* * ACPI CA does not define layers for non-ACPI CA drivers. * We define some here within the range provided. */ #define ACPI_AC_ADAPTER 0x00010000 #define ACPI_BATTERY 0x00020000 #define ACPI_BUS 0x00040000 #define ACPI_BUTTON 0x00080000 #define ACPI_EC 0x00100000 #define ACPI_FAN 0x00200000 #define ACPI_POWERRES 0x00400000 #define ACPI_PROCESSOR 0x00800000 #define ACPI_THERMAL 0x01000000 #define ACPI_TIMER 0x02000000 #define ACPI_OEM 0x04000000 /* * Constants for different interrupt models used with acpi_SetIntrModel(). */ #define ACPI_INTR_PIC 0 #define ACPI_INTR_APIC 1 #define ACPI_INTR_SAPIC 2 /* * Various features and capabilities for the acpi_get_features() method. * In particular, these are used for the ACPI 3.0 _PDC and _OSC methods. * See the Intel document titled "Intel Processor Vendor-Specific ACPI", * number 302223-007. */ #define ACPI_CAP_PERF_MSRS (1 << 0) /* Intel SpeedStep PERF_CTL MSRs */ #define ACPI_CAP_C1_IO_HALT (1 << 1) /* Intel C1 "IO then halt" sequence */ #define ACPI_CAP_THR_MSRS (1 << 2) /* Intel OnDemand throttling MSRs */ #define ACPI_CAP_SMP_SAME (1 << 3) /* MP C1, Px, and Tx (all the same) */ #define ACPI_CAP_SMP_SAME_C3 (1 << 4) /* MP C2 and C3 (all the same) */ #define ACPI_CAP_SMP_DIFF_PX (1 << 5) /* MP Px (different, using _PSD) */ #define ACPI_CAP_SMP_DIFF_CX (1 << 6) /* MP Cx (different, using _CSD) */ #define ACPI_CAP_SMP_DIFF_TX (1 << 7) /* MP Tx (different, using _TSD) */ #define ACPI_CAP_SMP_C1_NATIVE (1 << 8) /* MP C1 support other than halt */ #define ACPI_CAP_SMP_C3_NATIVE (1 << 9) /* MP C2 and C3 support */ #define ACPI_CAP_PX_HW_COORD (1 << 11) /* Intel P-state HW coordination */ #define ACPI_CAP_INTR_CPPC (1 << 12) /* Native Interrupt Handling for Collaborative Processor Performance Control notifications */ #define ACPI_CAP_HW_DUTY_C (1 << 13) /* Hardware Duty Cycling */ /* * Quirk flags. * * ACPI_Q_BROKEN: Disables all ACPI support. * ACPI_Q_TIMER: Disables support for the ACPI timer. * ACPI_Q_MADT_IRQ0: Specifies that ISA IRQ 0 is wired up to pin 0 of the * first APIC and that the MADT should force that by ignoring the PC-AT * compatible flag and ignoring overrides that redirect IRQ 0 to pin 2. */ extern int acpi_quirks; #define ACPI_Q_OK 0 #define ACPI_Q_BROKEN (1 << 0) #define ACPI_Q_TIMER (1 << 1) #define ACPI_Q_MADT_IRQ0 (1 << 2) /* * Note that the low ivar values are reserved to provide * interface compatibility with ISA drivers which can also * attach to ACPI. */ #define ACPI_IVAR_HANDLE 0x100 #define ACPI_IVAR_UNUSED 0x101 /* Unused/reserved. */ #define ACPI_IVAR_PRIVATE 0x102 #define ACPI_IVAR_FLAGS 0x103 /* * Accessor functions for our ivars. Default value for BUS_READ_IVAR is * (type) 0. The accessor functions don't check return values. */ #define __ACPI_BUS_ACCESSOR(varp, var, ivarp, ivar, type) \ \ static __inline type varp ## _get_ ## var(device_t dev) \ { \ uintptr_t v = 0; \ BUS_READ_IVAR(device_get_parent(dev), dev, \ ivarp ## _IVAR_ ## ivar, &v); \ return ((type) v); \ } \ \ static __inline void varp ## _set_ ## var(device_t dev, type t) \ { \ uintptr_t v = (uintptr_t) t; \ BUS_WRITE_IVAR(device_get_parent(dev), dev, \ ivarp ## _IVAR_ ## ivar, v); \ } __ACPI_BUS_ACCESSOR(acpi, handle, ACPI, HANDLE, ACPI_HANDLE) __ACPI_BUS_ACCESSOR(acpi, private, ACPI, PRIVATE, void *) __ACPI_BUS_ACCESSOR(acpi, flags, ACPI, FLAGS, int) void acpi_fake_objhandler(ACPI_HANDLE h, void *data); static __inline device_t acpi_get_device(ACPI_HANDLE handle) { void *dev = NULL; AcpiGetData(handle, acpi_fake_objhandler, &dev); return ((device_t)dev); } static __inline ACPI_OBJECT_TYPE acpi_get_type(device_t dev) { ACPI_HANDLE h; ACPI_OBJECT_TYPE t; if ((h = acpi_get_handle(dev)) == NULL) return (ACPI_TYPE_NOT_FOUND); if (ACPI_FAILURE(AcpiGetType(h, &t))) return (ACPI_TYPE_NOT_FOUND); return (t); } /* Find the difference between two PM tick counts. */ static __inline uint32_t acpi_TimerDelta(uint32_t end, uint32_t start) { if (end < start && (AcpiGbl_FADT.Flags & ACPI_FADT_32BIT_TIMER) == 0) end |= 0x01000000; return (end - start); } #ifdef ACPI_DEBUGGER void acpi_EnterDebugger(void); #endif #ifdef ACPI_DEBUG #include #define STEP(x) do {printf x, printf("\n"); cngetc();} while (0) #else #define STEP(x) #endif #define ACPI_VPRINT(dev, acpi_sc, x...) do { \ if (acpi_get_verbose(acpi_sc)) \ device_printf(dev, x); \ } while (0) /* Values for the first status word returned by _OSC. */ #define ACPI_OSC_FAILURE (1 << 1) #define ACPI_OSC_BAD_UUID (1 << 2) #define ACPI_OSC_BAD_REVISION (1 << 3) #define ACPI_OSC_CAPS_MASKED (1 << 4) #define ACPI_DEVINFO_PRESENT(x, flags) \ (((x) & (flags)) == (flags)) #define ACPI_DEVICE_PRESENT(x) \ ACPI_DEVINFO_PRESENT(x, ACPI_STA_DEVICE_PRESENT | \ ACPI_STA_DEVICE_FUNCTIONING) #define ACPI_BATTERY_PRESENT(x) \ ACPI_DEVINFO_PRESENT(x, ACPI_STA_DEVICE_PRESENT | \ ACPI_STA_DEVICE_FUNCTIONING | ACPI_STA_BATTERY_PRESENT) /* Callback function type for walking subtables within a table. */ typedef void acpi_subtable_handler(ACPI_SUBTABLE_HEADER *, void *); BOOLEAN acpi_DeviceIsPresent(device_t dev); BOOLEAN acpi_BatteryIsPresent(device_t dev); ACPI_STATUS acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result); ACPI_BUFFER *acpi_AllocBuffer(int size); ACPI_STATUS acpi_ConvertBufferToInteger(ACPI_BUFFER *bufp, UINT32 *number); ACPI_STATUS acpi_GetInteger(ACPI_HANDLE handle, char *path, UINT32 *number); ACPI_STATUS acpi_SetInteger(ACPI_HANDLE handle, char *path, UINT32 number); ACPI_STATUS acpi_ForeachPackageObject(ACPI_OBJECT *obj, void (*func)(ACPI_OBJECT *comp, void *arg), void *arg); ACPI_STATUS acpi_FindIndexedResource(ACPI_BUFFER *buf, int index, ACPI_RESOURCE **resp); ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res); UINT8 acpi_DSMQuery(ACPI_HANDLE h, uint8_t *uuid, int revision); ACPI_STATUS acpi_EvaluateDSM(ACPI_HANDLE handle, uint8_t *uuid, int revision, uint64_t function, union acpi_object *package, ACPI_BUFFER *out_buf); ACPI_STATUS acpi_EvaluateOSC(ACPI_HANDLE handle, uint8_t *uuid, int revision, int count, uint32_t *caps_in, uint32_t *caps_out, bool query); ACPI_STATUS acpi_OverrideInterruptLevel(UINT32 InterruptNumber); ACPI_STATUS acpi_SetIntrModel(int model); int acpi_ReqSleepState(struct acpi_softc *sc, int state); int acpi_AckSleepState(struct apm_clone_data *clone, int error); ACPI_STATUS acpi_SetSleepState(struct acpi_softc *sc, int state); int acpi_wake_set_enable(device_t dev, int enable); int acpi_parse_prw(ACPI_HANDLE h, struct acpi_prw_data *prw); ACPI_STATUS acpi_Startup(void); void acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify); int acpi_bus_alloc_gas(device_t dev, int *type, int *rid, ACPI_GENERIC_ADDRESS *gas, struct resource **res, u_int flags); void acpi_walk_subtables(void *first, void *end, acpi_subtable_handler *handler, void *arg); int acpi_MatchHid(ACPI_HANDLE h, const char *hid); #define ACPI_MATCHHID_NOMATCH 0 #define ACPI_MATCHHID_HID 1 #define ACPI_MATCHHID_CID 2 struct acpi_parse_resource_set { void (*set_init)(device_t dev, void *arg, void **context); void (*set_done)(device_t dev, void *context); void (*set_ioport)(device_t dev, void *context, uint64_t base, uint64_t length); void (*set_iorange)(device_t dev, void *context, uint64_t low, uint64_t high, uint64_t length, uint64_t align); void (*set_memory)(device_t dev, void *context, uint64_t base, uint64_t length); void (*set_memoryrange)(device_t dev, void *context, uint64_t low, uint64_t high, uint64_t length, uint64_t align); void (*set_irq)(device_t dev, void *context, uint8_t *irq, int count, int trig, int pol); void (*set_ext_irq)(device_t dev, void *context, uint32_t *irq, int count, int trig, int pol); void (*set_drq)(device_t dev, void *context, uint8_t *drq, int count); void (*set_start_dependent)(device_t dev, void *context, int preference); void (*set_end_dependent)(device_t dev, void *context); }; extern struct acpi_parse_resource_set acpi_res_parse_set; int acpi_identify(void); void acpi_config_intr(device_t dev, ACPI_RESOURCE *res); #ifdef INTRNG int acpi_map_intr(device_t dev, u_int irq, ACPI_HANDLE handle); #endif ACPI_STATUS acpi_lookup_irq_resource(device_t dev, int rid, struct resource *res, ACPI_RESOURCE *acpi_res); ACPI_STATUS acpi_parse_resources(device_t dev, ACPI_HANDLE handle, struct acpi_parse_resource_set *set, void *arg); struct resource *acpi_alloc_sysres(device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags); /* ACPI event handling */ UINT32 acpi_event_power_button_sleep(void *context); UINT32 acpi_event_power_button_wake(void *context); UINT32 acpi_event_sleep_button_sleep(void *context); UINT32 acpi_event_sleep_button_wake(void *context); #define ACPI_EVENT_PRI_FIRST 0 #define ACPI_EVENT_PRI_DEFAULT 10000 #define ACPI_EVENT_PRI_LAST 20000 typedef void (*acpi_event_handler_t)(void *, int); EVENTHANDLER_DECLARE(acpi_sleep_event, acpi_event_handler_t); EVENTHANDLER_DECLARE(acpi_wakeup_event, acpi_event_handler_t); /* Device power control. */ ACPI_STATUS acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable); ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state); int acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate); /* APM emulation */ void acpi_apm_init(struct acpi_softc *); /* Misc. */ static __inline struct acpi_softc * acpi_device_get_parent_softc(device_t child) { device_t parent; parent = device_get_parent(child); if (parent == NULL) return (NULL); return (device_get_softc(parent)); } static __inline int acpi_get_verbose(struct acpi_softc *sc) { if (sc) return (sc->acpi_verbose); return (0); } char *acpi_name(ACPI_HANDLE handle); int acpi_avoid(ACPI_HANDLE handle); int acpi_disabled(char *subsys); int acpi_machdep_init(device_t dev); void acpi_install_wakeup_handler(struct acpi_softc *sc); int acpi_sleep_machdep(struct acpi_softc *sc, int state); int acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result, int intr_enabled); int acpi_table_quirks(int *quirks); int acpi_machdep_quirks(int *quirks); uint32_t hpet_get_uid(device_t dev); /* Battery Abstraction. */ struct acpi_battinfo; int acpi_battery_register(device_t dev); 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); /* Embedded controller. */ void acpi_ec_ecdt_probe(device_t); /* AC adapter interface. */ int acpi_acad_get_acline(int *); /* Package manipulation convenience functions. */ #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); int acpi_PkgFFH_IntelCpu(ACPI_OBJECT *res, int idx, int *vendor, int *class, uint64_t *address, int *accsize); ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj); /* * Base level for BUS_ADD_CHILD. Special devices are added at orders less * than this, and normal devices at or above this level. This keeps the * probe order sorted so that things like sysresource are available before * their children need them. */ #define ACPI_DEV_BASE_ORDER 100 /* Default maximum number of tasks to enqueue. */ #ifndef ACPI_MAX_TASKS #define ACPI_MAX_TASKS MAX(32, MAXCPU * 4) #endif /* Default number of task queue threads to start. */ #ifndef ACPI_MAX_THREADS #define ACPI_MAX_THREADS 3 #endif /* Use the device logging level for ktr(4). */ #define KTR_ACPI KTR_DEV SYSCTL_DECL(_debug_acpi); /* * Parse and use proximity information in SRAT and SLIT. */ int acpi_pxm_init(int ncpus, vm_paddr_t maxphys); void acpi_pxm_parse_tables(void); void acpi_pxm_set_mem_locality(void); void acpi_pxm_set_cpu_locality(void); int acpi_pxm_get_cpu_locality(int apic_id); void acpi_pxm_free(void); /* * Map a PXM to a VM domain. * * Returns the VM domain ID if found, or -1 if not found / invalid. */ int acpi_map_pxm_to_vm_domainid(int pxm); int acpi_get_cpus(device_t dev, device_t child, enum cpu_sets op, size_t setsize, cpuset_t *cpuset); int acpi_get_domain(device_t dev, device_t child, int *domain); #ifdef __aarch64__ /* * ARM specific ACPI interfaces, relating to IORT table. */ int acpi_iort_map_pci_msi(u_int seg, u_int rid, u_int *xref, u_int *devid); int acpi_iort_its_lookup(u_int its_id, u_int *xref, int *pxm); #endif #endif /* _KERNEL */ #endif /* !_ACPIVAR_H_ */ Index: head/usr.sbin/acpi/acpiconf/acpiconf.c =================================================================== --- head/usr.sbin/acpi/acpiconf/acpiconf.c (revision 358094) +++ head/usr.sbin/acpi/acpiconf/acpiconf.c (revision 358095) @@ -1,276 +1,303 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1999 Mitsuru IWASAKI * 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. * * $Id: acpiconf.c,v 1.5 2000/08/08 14:12:19 iwasaki Exp $ * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #define ACPIDEV "/dev/acpi" static int acpifd; static void acpi_init(void) { acpifd = open(ACPIDEV, O_RDWR); if (acpifd == -1) acpifd = open(ACPIDEV, O_RDONLY); if (acpifd == -1) err(EX_OSFILE, ACPIDEV); } /* Prepare to sleep and then wait for the signal that sleeping can occur. */ static void acpi_sleep(int sleep_type) { int ret; /* Notify OS that we want to sleep. devd(8) gets this notify. */ ret = ioctl(acpifd, ACPIIO_REQSLPSTATE, &sleep_type); if (ret != 0) err(EX_IOERR, "request sleep type (%d) failed", sleep_type); } /* Ack or abort a pending suspend request. */ static void acpi_sleep_ack(int err_val) { int ret; ret = ioctl(acpifd, ACPIIO_ACKSLPSTATE, &err_val); if (ret != 0) err(EX_IOERR, "ack sleep type failed"); } /* should be a acpi define, but doesn't appear to be */ #define UNKNOWN_CAP 0xffffffff #define UNKNOWN_VOLTAGE 0xffffffff static int acpi_battinfo(int num) { union acpi_battery_ioctl_arg battio; const char *pwr_units; int hours, min, amp; uint32_t volt; if (num < 0 || num > 64) errx(EX_USAGE, "invalid battery %d", 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; battio.unit = num; if (ioctl(acpifd, ACPIIO_BATT_GET_BST, &battio) == -1) err(EX_IOERR, "get battery status (%d) failed", num); if (battio.bst.state != ACPI_BATT_STAT_NOT_PRESENT) volt = battio.bst.volt; /* Print current battery state information. */ battio.unit = num; if (ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) == -1) err(EX_IOERR, "get battery user info (%d) failed", num); if (battio.battinfo.state != ACPI_BATT_STAT_NOT_PRESENT) { const char *state; switch (battio.battinfo.state & ACPI_BATT_STAT_BST_MASK) { case 0: state = "high"; break; case ACPI_BATT_STAT_DISCHARG: state = "discharging"; break; case ACPI_BATT_STAT_CHARGING: state = "charging"; break; case ACPI_BATT_STAT_CRITICAL: state = "critical"; break; case ACPI_BATT_STAT_DISCHARG | ACPI_BATT_STAT_CRITICAL: state = "critical discharging"; break; case ACPI_BATT_STAT_CHARGING | ACPI_BATT_STAT_CRITICAL: state = "critical charging"; break; default: state = "invalid"; } printf("State:\t\t\t%s\n", state); if (battio.battinfo.cap == -1) printf("Remaining capacity:\tunknown\n"); else printf("Remaining capacity:\t%d%%\n", battio.battinfo.cap); if (battio.battinfo.min == -1) printf("Remaining time:\t\tunknown\n"); else { hours = battio.battinfo.min / 60; min = battio.battinfo.min % 60; printf("Remaining time:\t\t%d:%02d\n", hours, min); } if (battio.battinfo.rate == -1) printf("Present rate:\t\tunknown\n"); else if (amp && volt != UNKNOWN_VOLTAGE) { printf("Present rate:\t\t%d mA (%d mW)\n", battio.battinfo.rate, battio.battinfo.rate * volt / 1000); } else printf("Present rate:\t\t%d %s\n", battio.battinfo.rate, pwr_units); } else printf("State:\t\t\tnot present\n"); /* Print battery voltage information. */ if (volt == UNKNOWN_VOLTAGE) printf("Present voltage:\tunknown\n"); else printf("Present voltage:\t%d mV\n", volt); return (0); } static void usage(const char* prog) { printf("usage: %s [-h] [-i batt] [-k ack] [-s 1-4]\n", prog); exit(0); } int main(int argc, char *argv[]) { char *prog, *end; int c, sleep_type, battery, ack; int iflag = 0, kflag = 0, sflag = 0; prog = argv[0]; if (argc < 2) usage(prog); /* NOTREACHED */ sleep_type = -1; acpi_init(); while ((c = getopt(argc, argv, "hi:k:s:")) != -1) { switch (c) { case 'i': iflag = 1; battery = strtol(optarg, &end, 10); if ((size_t)(end - optarg) != strlen(optarg)) errx(EX_USAGE, "invalid battery"); break; case 'k': kflag = 1; ack = strtol(optarg, &end, 10); if ((size_t)(end - optarg) != strlen(optarg)) errx(EX_USAGE, "invalid ack argument"); break; case 's': sflag = 1; if (optarg[0] == 'S') optarg++; sleep_type = strtol(optarg, &end, 10); if ((size_t)(end - optarg) != strlen(optarg)) errx(EX_USAGE, "invalid sleep type"); if (sleep_type < 1 || sleep_type > 4) errx(EX_USAGE, "invalid sleep type (%d)", sleep_type); break; case 'h': default: usage(prog); /* NOTREACHED */ } } argc -= optind; argv += optind; if (iflag != 0 && kflag != 0 && sflag != 0) errx(EX_USAGE, "-i, -k and -s are mutually exclusive"); if (iflag != 0) { if (kflag != 0) errx(EX_USAGE, "-i and -k are mutually exclusive"); if (sflag != 0) errx(EX_USAGE, "-i and -s are mutually exclusive"); acpi_battinfo(battery); } if (kflag != 0) { if (sflag != 0) errx(EX_USAGE, "-k and -s are mutually exclusive"); acpi_sleep_ack(ack); } if (sflag != 0) acpi_sleep(sleep_type); close(acpifd); exit (0); }