Index: sys/dev/acpica/acpi_cmbat.c =================================================================== --- sys/dev/acpica/acpi_cmbat.c +++ sys/dev/acpica/acpi_cmbat.c @@ -62,6 +62,7 @@ #define ACPI_BATTERY_BST_CHANGE 0x80 #define ACPI_BATTERY_BIF_CHANGE 0x81 #define ACPI_BATTERY_BIX_CHANGE ACPI_BATTERY_BIF_CHANGE +#define ACPI_BATTERY_BTP_WARNING_LEVEL 20 struct acpi_cmbat_softc { device_t dev; @@ -70,6 +71,8 @@ struct acpi_bix bix; struct acpi_bst bst; struct timespec bst_lastupdated; + BOOLEAN acpi_btp_exists; + int btp_warning_level; }; ACPI_SERIAL_DECL(cmbat, "ACPI cmbat"); @@ -87,6 +90,8 @@ 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_btp_set(void *, int); +static int acpi_cmbat_btp_sysctl(SYSCTL_HANDLER_ARGS); static void acpi_cmbat_init_battery(void *arg); static device_method_t acpi_cmbat_methods[] = { @@ -118,12 +123,12 @@ { static char *cmbat_ids[] = { "PNP0C0A", NULL }; int rv; - + if (acpi_disabled("cmbat")) - return (ENXIO); + 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"); + if(rv <= 0) + device_set_desc(dev, "ACPI Control Method Battery"); return (rv); } @@ -132,6 +137,7 @@ { int error; ACPI_HANDLE handle; + ACPI_HANDLE tmp; struct acpi_cmbat_softc *sc; sc = device_get_softc(dev); @@ -146,6 +152,23 @@ return (error); } + /* A potential battery warning level sysctl. */ + + ACPI_SERIAL_BEGIN(cmbat); + if(ACPI_SUCCESS(acpi_GetHandleInScope(handle, "_BTP", &tmp))) { + + sc->acpi_btp_exists = TRUE; + sc->btp_warning_level = ACPI_BATTERY_BTP_WARNING_LEVEL; + + struct sysctl_oid *cmbat_oid = device_get_sysctl_tree(dev); + SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(cmbat_oid), OID_AUTO, + "warning_level", CTLTYPE_INT | CTLFLAG_RW, 0, 0, + acpi_cmbat_btp_sysctl, "I" ,"battery warning level"); + } + else + sc->acpi_btp_exists = FALSE; + ACPI_SERIAL_END(cmbat); + /* * Install a system notify handler in addition to the device notify. * Toshiba notebook uses this alternate notify for its battery. @@ -526,6 +549,75 @@ } static void +acpi_cmbat_btp_set(void *arg, int warning) +{ + + struct acpi_cmbat_softc *sc; + device_t dev; + ACPI_HANDLE h; + ACPI_STATUS as; + uint64_t newtp; + + ACPI_SERIAL_ASSERT(cmbat); + dev = arg; + sc = device_get_softc(dev); + h = acpi_get_handle(dev); + + newtp = sc->bix.lfcap * warning / 100; + as = acpi_SetInteger(h, "_BTP", (uint32_t) newtp); + + if (ACPI_FAILURE(as)) { + ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), + "error setting _BTP --%s\n", AcpiFormatException(as)); + sc->btp_warning_level = 0; + } + else + sc->btp_warning_level = warning; +} + +static int +acpi_cmbat_btp_sysctl(SYSCTL_HANDLER_ARGS) +{ + + struct sysctl_oid *parent; + struct acpi_cmbat_softc *sc; + device_t dev; + ACPI_HANDLE h; + long battery_index; + + + ACPI_SERIAL_BEGIN(cmbat); + + parent = SYSCTL_PARENT(oidp); + battery_index = strtol(parent->oid_name, NULL, 0); + dev = devclass_get_device(acpi_cmbat_devclass, (int) battery_index); + + h = acpi_get_handle(dev); + sc = device_get_softc(dev); + + if(req->newptr && acpi_BatteryIsPresent(dev)) { + /* Write request */ + + SYSCTL_IN(req, &sc->btp_warning_level, sizeof(sc->btp_warning_level)); + /* correct bogus writes */ + if(sc->btp_warning_level < 0 || sc->btp_warning_level > 100) + sc->btp_warning_level = ACPI_BATTERY_BTP_WARNING_LEVEL; + + acpi_cmbat_btp_set(dev, sc->btp_warning_level); + } + + else if(req->newptr) /* write request w/o battery */ + sc->btp_warning_level = 0; + + else /* read request */ + SYSCTL_OUT(req, &sc->btp_warning_level, sizeof(sc->btp_warning_level)); + + ACPI_SERIAL_END(cmbat); + return (0); +} + + +static void acpi_cmbat_init_battery(void *arg) { struct acpi_cmbat_softc *sc; @@ -578,6 +670,13 @@ valid = acpi_battery_bst_valid(&sc->bst) && acpi_battery_bix_valid(&sc->bix); + + /* set battery warning level */ + if(valid && sc->acpi_btp_exists) + acpi_cmbat_btp_set(dev, sc->btp_warning_level); + else + sc->btp_warning_level = 0; + ACPI_SERIAL_END(cmbat); if (valid)