fix(drivers/power_monitor): reject out-of-range INA226 cal and INA228 index (#28316)

CAL = 0.00512 / (current_lsb * R_shunt) is a 16-bit register. Legal
INA226_CURRENT × INA226_SHUNT combos overflow it, and the float-to-uint16
conversion is undefined. Fail start with the offending params rather than
program a wrapped value.

ina228 -t outside 1-3 was accepted; Battery() silently clamps to index 1.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
This commit is contained in:
Jacob Dahl
2026-08-20 19:42:22 -06:00
committed by GitHub
parent 0c8f4527c0
commit 89e1b24cb2
2 changed files with 35 additions and 4 deletions
+28 -3
View File
@@ -53,12 +53,28 @@ INA226::INA226(const I2CSPIDriverConfig &config, int battery_index)
const float shunt_resistance = _param_ina226_shunt.get();
_current_lsb = max_current / 32768.f; // From datasheet: current_lsb = max_current / 2^15
_calibration = static_cast<uint16_t>(CAL_K / (_current_lsb * shunt_resistance));
// CAL is a 16-bit register. Legal INA226_CURRENT × INA226_SHUNT combos can
// overflow it; converting that float to uint16_t is undefined.
const float denom = _current_lsb * shunt_resistance;
const float cal = (denom > 0.f) ? (CAL_K / denom) : 0.f;
if ((cal < 1.f) || (cal > 65535.f)) {
PX4_ERR("INA226 calibration %.1f out of uint16 range (INA226_CURRENT=%.4f INA226_SHUNT=%.9f)",
(double)cal, (double)max_current, (double)shunt_resistance);
_current_lsb = 0.f;
_calibration = 0;
} else {
_calibration = static_cast<uint16_t>(cal);
}
_config_value = MODE_SHUNT_BUS_CONT | VSHCT_588US | VBUSCT_588US | AVERAGES_64;
// Publish an initial disconnected status so the first instance grabs uORB instance 0 immediately.
_battery.updateAndPublishBatteryStatus(hrt_absolute_time());
if (_calibration != 0) {
// Publish an initial disconnected status so the first instance grabs uORB instance 0 immediately.
_battery.updateAndPublishBatteryStatus(hrt_absolute_time());
}
// Let the lower I2C layer absorb transient bus errors before we see them.
I2C::_retries = 5;
@@ -75,6 +91,10 @@ INA226::~INA226()
int INA226::init()
{
if (_calibration == 0) {
return PX4_ERROR;
}
if (I2C::init() != PX4_OK) {
return PX4_ERROR;
}
@@ -313,6 +333,11 @@ I2CSPIDriverBase *INA226::instantiate(const I2CSPIDriverConfig &config, int /*ru
return nullptr;
}
if (instance->_calibration == 0) {
delete instance;
return nullptr;
}
if (instance->init() == PX4_OK) {
instance->ScheduleNow();
@@ -82,7 +82,7 @@ this flag set, the battery must be plugged in before starting the driver.
PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(true, false);
PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS(0x45);
PRINT_MODULE_USAGE_PARAMS_I2C_KEEP_RUNNING_FLAG();
PRINT_MODULE_USAGE_PARAM_INT('t', 1, 1, 3, "battery index for calibration values (1 or 3)", true);
PRINT_MODULE_USAGE_PARAM_INT('t', 1, 1, 3, "battery index for calibration values (1-3)", true);
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
}
@@ -101,6 +101,12 @@ ina228_main(int argc, char *argv[])
switch (ch) {
case 't': // battery index
cli.custom1 = (int)strtol(cli.optArg(), NULL, 0);
if (cli.custom1 < 1 || cli.custom1 > 3) {
PX4_ERR("index must be 1-3");
return -1;
}
break;
}
}