static void acpi_cmbat_init_battery(void *arg) { struct acpi_cmbat_softc *sc; int retry, valid; device_t dev; dev = (device_t)arg; sc = device_get_softc(dev); ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), "battery initialization 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 (!device_is_attached(dev)) return; if (!acpi_BatteryIsPresent(dev)) continue; /* * Only query the battery if this is the first try or the specific * type of info is still invalid. */ ACPI_SERIAL_BEGIN(cmbat); 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); valid = acpi_battery_bst_valid(&sc->bst) && acpi_battery_bif_valid(&sc->bif); 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"); } else { ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), "battery initialization done, tried %d times\n", retry + 1); } }
/* 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_min, valid_rate, valid_units; devclass_t batt_dc; device_t batt_dev; struct acpi_bst *bst; struct acpi_bif *bif; 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. */ bst = kmalloc(devcount * sizeof(*bst), M_TEMP, M_WAITOK | M_ZERO); bi = kmalloc(devcount * sizeof(*bi), M_TEMP, M_WAITOK | M_ZERO); bif = kmalloc(sizeof(*bif), 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; 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. Note that * acpi_BatteryIsPresent() is not enough because smart batteries only * return that the device is present. */ if (!acpi_BatteryIsPresent(batt_dev) || ACPI_BATT_GET_STATUS(batt_dev, &bst[i]) != 0 || ACPI_BATT_GET_INFO(batt_dev, bif) != 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)) 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; } /* * The calculation above may set bif->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)) continue; /* Calculate percent capacity remaining. */ bi[i].cap = (100 * bst[i].cap) / bif->lfcap; /* * 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 (bi[i].cap > 100) bi[i].cap = 100; /* * 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; /* * Some DSDTs report a negative 16-bit value for the rate and/or * report 0 as 65536. */ if (acpi_quirks & ACPI_Q_BATT_RATE_ABS && bif->units == ACPI_BIF_UNITS_MA && bst[i].rate != ACPI_BATT_UNKNOWN && (int16_t)bst[i].rate < 0) bst[i].rate = abs((int16_t)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_cap = 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; /* If this battery is not present, don't use its capacity. */ if (bi[i].cap != -1) total_cap += bi[i].cap; } /* * 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 / valid_units; 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) kfree(bi, M_TEMP); if (bif) kfree(bif, M_TEMP); if (bst) kfree(bst, M_TEMP); return (error); }