Bogus battery time left (nine hundred hours) – POWER_SUPPLY_TIME_TO_EMPTY_NOW taken as minutes when it is seconds: driver compatibility?
- Dominant language
- C
- Stars
- 653
- Forks
- 254
- PR merge metrics
- No merged PRs in 30d
Description
I have a Google Hana device with a battery which is `compatible = "sbs,sbs-battery"`. This yields a sysfs entry as `/sys/class/power_supply/sbs-6-000b`:
```
POWER_SUPPLY_NAME=sbs-6-000b
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_STATUS=Discharging
POWER_SUPPLY_CAPACITY_LEVEL=Normal
POWER_SUPPLY_HEALTH=Unknown
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_TECHNOLOGY=Li-poly
POWER_SUPPLY_CYCLE_COUNT=7
POWER_SUPPLY_VOLTAGE_NOW=11810000
POWER_SUPPLY_CURRENT_NOW=-202000
POWER_SUPPLY_CURRENT_AVG=-170000
POWER_SUPPLY_CAPACITY=71
POWER_SUPPLY_CAPACITY_ERROR_MARGIN=1
POWER_SUPPLY_TEMP=272
POWER_SUPPLY_TIME_TO_EMPTY_NOW=48300
POWER_SUPPLY_TIME_TO_EMPTY_AVG=55140
POWER_SUPPLY_TIME_TO_FULL_AVG=3932100
POWER_SUPPLY_SERIAL_NUMBER=049a
POWER_SUPPLY_VOLTAGE_MIN_DESIGN=11250000
POWER_SUPPLY_VOLTAGE_MAX_DESIGN=11250000
POWER_SUPPLY_ENERGY_NOW=28930000
POWER_SUPPLY_ENERGY_FULL=41760000
POWER_SUPPLY_ENERGY_FULL_DESIGN=42000000
POWER_SUPPLY_CHARGE_NOW=2603000
POWER_SUPPLY_CHARGE_FULL=3648000
POWER_SUPPLY_CHARGE_FULL_DESIGN=3735000
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT_MAX=1839000
POWER_SUPPLY_CONSTANT_CHARGE_VOLTAGE_MAX=12900000
POWER_SUPPLY_MANUFACTURE_YEAR=2019
POWER_SUPPLY_MANUFACTURE_MONTH=7
POWER_SUPPLY_MANUFACTURE_DAY=29
POWER_SUPPLY_MANUFACTURER=SMP
POWER_SUPPLY_MODEL_NAME=L17M3PB0
```
But attaching i3status to it gives results of "BAT 69.56% 962:00", "974:00".
It is a big battery and it is discharging at just 1.94W, but hundreds of hours is, well.
A quick grep-around shows that i3status parses `POWER_SUPPLY_TIME_TO_EMPTY_NOW` as `batt_info->seconds_remaining = abs(atoi(walk + 1)) * 60;`.
`drivers/power/supply/sbs-battery.c` in linux 6.6.11 shows
```c
static void sbs_unit_adjustment(struct i2c_client *client,
enum power_supply_property psp, union power_supply_propval *val)
{
#define BASE_UNIT_CONVERSION 1000
#define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION)
#define TIME_UNIT_CONVERSION 60
#define TEMP_KELVIN_TO_CELSIUS 2731
// ...
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
/* sbs provides time to empty and time to full in minutes.
* Convert to seconds
*/
val->intval *= TIME_UNIT_CONVERSION;
break;
```
and being off factor of 60 would put it at around 15.7h, which I'd be willing to buy (this is a Lenovo 300e Chromebook 2nd Gen MTK; I also have a Lenovo 300e Chromebook 2nd Gen, which is a similar Intel platform, and it gets around that when it hits <2W).
Additional survey of the non-staging results I got for `grep -rI TIME_TO_EMPTY_NOW` shows:
```c
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {
int percent = twl4030_madc_bat_voltscale(bat,
twl4030_madc_bat_get_voltage(bat));
/* in mAh */
int chg = (percent * (bat->pdata->capacity/1000))/100;
/* assume discharge with 400 mA (ca. 1.5W) */
val->intval = (3600l * chg) / 400;
break;
drivers/power/supply/twl4030_madc_battery.c lines 110-162/272 byte 4435/7515 59% (press RETURN)
```
(idk)
```c
static int rn5t618_battery_tte(struct rn5t618_power_info *info,
union power_supply_propval *val)
{
u16 res;
int ret;
ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_EMPTY_H, &res);
if (ret)
return ret;
if (res == 65535)
return -ENODATA;
val->intval = res * 60;
return 0;
}
drivers/power/supply/rn5t618_power.c lines 226-278/828 byte 6289/19922 32% (press RETURN)
```
(EMPTY_H * 60 => minutes)
```c
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = regmap_read(map, MAX17042_TTE, &data);
if (ret < 0)
return ret;
val->intval = data * 5625 / 1000;
break;
drivers/power/supply/max17042_battery.c lines 380-432/1225 byte 10904/33247 33% (press RETURN)
```
(no clue)
```c
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
val->intval = di->life_sec;
break;
drivers/power/supply/ds2760_battery.c lines 524-576/808 byte 16594/22502 74% (press RETURN)
```
(seconds)
```c
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
if (cw_battery_valid_time_to_empty(cw_bat))
val->intval = cw_bat->time_to_empty;
else
val->intval = 0;
break;
drivers/power/supply/cw2015_battery.c lines 445-497/759 byte 12373/19482 64% (press RETURN)
```
(`time_to_empty` is unannotated – no clue)
```c
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = bq27xxx_simple_value(di->cache.time_to_empty, val);
break;
drivers/power/supply/bq27xxx_battery.c lines 1990-2042/2167 byte 58542/62305 94% (press RETURN)
```
```c
/*
* Read a time register.
* Return < 0 if something fails.
*/
static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg)
{
int tval;
tval = bq27xxx_read(di, reg, false);
if (tval < 0) {
dev_dbg(di->dev, "error reading time register %02x: %d\n",
reg, tval);
return tval;
}
if (tval == 65535)
return -ENODATA;
return tval * 60;
}
```
(no clue)
```c
/* time */
info->units = APM_UNITS_MINS;
if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
if (!MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full) ||
!MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
info->time = time_to_full.intval / 60;
else
info->time = calculate_time(status.intval);
} else {
if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) ||
!MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
info->time = time_to_empty.intval / 60;
else
info->time = calculate_time(status.intval);
}
drivers/power/supply/apm_power.c lines 301-353/376 byte 9902/10386 95% (press RETURN)
```
(labelled as minutes, divides by 60 so can only be minutes really)
or, tabularly:
| seconds | minutes | idk |
| -- | -- | -- |
| sbs-battery | rn5t618_power | twl4030_madc_battery |
| ds2760_battery | apm_power | max17042_battery |
| | | cw2015_battery |
| | | bq27xxx_battery |
which is an even split (with ~100% of x86 platforms using the APM driver and ~100% of ARM platforms using the SBS driver 🙈).
So i3status's scaling produces obviously-incorrect results by a factor of 60.
Contributor guide
Research direction
Start with the battery parsing code that multiplies POWER_SUPPLY_TIME_TO_EMPTY_NOW by 60, then compare its expectation with the units exposed by drivers/power/supply/sbs-battery.c and the other drivers cited, including apm_power.c. Establish the power-supply contract before changing behavior, and verify that displayed battery times no longer have the reported factor-of-60 error without breaking drivers using seconds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, linux
- Domain
- operating-systems, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100