diff --git a/openpilot/cereal/log.capnp b/openpilot/cereal/log.capnp index 10b968b039043c..e1ebbaf5f835dc 100644 --- a/openpilot/cereal/log.capnp +++ b/openpilot/cereal/log.capnp @@ -178,6 +178,15 @@ struct InitData { wallTimeNanos @20 :UInt64; + ufsHealth @25 :UfsHealth; + + struct UfsHealth { + preEolInfo @0 :UInt8; + lifeTimeEstimateA @1 :UInt8; + lifeTimeEstimateB @2 :UInt8; + vendorHealthReport @3 :Data; + } + enum DeviceType { unknown @0; neo @1; diff --git a/openpilot/common/hardware/base.h b/openpilot/common/hardware/base.h index f4546adfa83676..8d2d34bfcf455b 100644 --- a/openpilot/common/hardware/base.h +++ b/openpilot/common/hardware/base.h @@ -1,15 +1,25 @@ #pragma once #include +#include #include #include +#include #include +#include #include "openpilot/cereal/gen/cpp/log.capnp.h" // no-op base hw class class HardwareNone { public: + struct UfsHealth { + uint8_t pre_eol_info; + uint8_t life_time_estimate_a; + uint8_t life_time_estimate_b; + std::vector vendor_health_report; + }; + static std::string get_name() { return ""; } static cereal::InitData::DeviceType get_device_type() { return cereal::InitData::DeviceType::UNKNOWN; } @@ -19,6 +29,8 @@ class HardwareNone { return {}; } + static std::optional get_ufs_health() { return std::nullopt; } + static void set_ir_power(int percentage) {} static bool PC() { return false; } diff --git a/openpilot/common/hardware/tici/hardware.h b/openpilot/common/hardware/tici/hardware.h index a1a8090c35a325..c031be24736e7e 100644 --- a/openpilot/common/hardware/tici/hardware.h +++ b/openpilot/common/hardware/tici/hardware.h @@ -1,16 +1,62 @@ #pragma once #include +#include +#include +#include +#include #include #include #include #include // for std::clamp +#include +#include #include "common/util.h" #include "common/hardware/base.h" class HardwareTici : public HardwareNone { public: + static std::optional get_ufs_health() { + constexpr unsigned long UFS_IOCTL_QUERY = 0x5388; + constexpr uint32_t UPIU_QUERY_OPCODE_READ_DESC = 0x1; + constexpr uint8_t QUERY_DESC_IDN_HEALTH = 0x9; + constexpr uint16_t QUERY_DESC_HEALTH_SIZE = 0x25; + + struct UfsQuery { + uint32_t opcode; + uint8_t idn; + uint8_t reserved; + uint16_t buf_size; + std::array buffer; + }; + static_assert(offsetof(UfsQuery, buffer) == 8); + + UfsQuery query = {}; + query.opcode = UPIU_QUERY_OPCODE_READ_DESC; + query.idn = QUERY_DESC_IDN_HEALTH; + query.buf_size = QUERY_DESC_HEALTH_SIZE; + + int fd = open("/dev/sda", O_RDONLY | O_CLOEXEC); + if (fd < 0) { + return std::nullopt; + } + + int ret = ioctl(fd, UFS_IOCTL_QUERY, &query); + close(fd); + if (ret != 0 || query.buf_size < 5 || query.buf_size > query.buffer.size() || + query.buffer[0] != query.buf_size || query.buffer[1] != QUERY_DESC_IDN_HEALTH) { + return std::nullopt; + } + + return UfsHealth{ + query.buffer[2], + query.buffer[3], + query.buffer[4], + std::vector(query.buffer.begin() + 5, query.buffer.begin() + query.buf_size), + }; + } + static std::string get_name() { static const std::string name = []() { std::string model = util::read_file("/sys/firmware/devicetree/base/model"); diff --git a/openpilot/system/loggerd/logger.cc b/openpilot/system/loggerd/logger.cc index 0ebe323939ab53..bfb51daa6d01a7 100644 --- a/openpilot/system/loggerd/logger.cc +++ b/openpilot/system/loggerd/logger.cc @@ -39,6 +39,14 @@ kj::Array logger_build_init_data() { init.setKernelVersion(util::read_file("/proc/version")); init.setOsVersion(util::read_file("/VERSION")); + if (auto health = Hardware::get_ufs_health()) { + auto ufs_health = init.initUfsHealth(); + ufs_health.setPreEolInfo(health->pre_eol_info); + ufs_health.setLifeTimeEstimateA(health->life_time_estimate_a); + ufs_health.setLifeTimeEstimateB(health->life_time_estimate_b); + ufs_health.setVendorHealthReport(capnp::Data::Reader(health->vendor_health_report.data(), health->vendor_health_report.size())); + } + // log params Params params(util::getenv("PARAMS_COPY_PATH", "")); std::map params_map = params.readAll(); diff --git a/openpilot/system/loggerd/tests/test_loggerd.py b/openpilot/system/loggerd/tests/test_loggerd.py index 618e3d00161a37..32d64367e76887 100644 --- a/openpilot/system/loggerd/tests/test_loggerd.py +++ b/openpilot/system/loggerd/tests/test_loggerd.py @@ -178,6 +178,15 @@ def test_init_data_values(self): assert initData.dirty != bool(os.environ["CLEAN"]) assert initData.version == get_version() + if TICI: + assert initData._has("ufsHealth") + assert initData.ufsHealth.preEolInfo in (1, 2, 3) + assert 1 <= initData.ufsHealth.lifeTimeEstimateA <= 11 + assert 1 <= initData.ufsHealth.lifeTimeEstimateB <= 11 + assert len(initData.ufsHealth.vendorHealthReport) == 32 + else: + assert not initData._has("ufsHealth") + if os.path.isfile("/proc/cmdline"): with open("/proc/cmdline") as f: assert list(initData.kernelArgs) == f.read().strip().split(" ")