add firmware inventory support
Add support to display the firmware version using the Firmware version
register from EM configuration. The version will be exposed as
xyz.openbmc_project.Software.Version interface on the Dbus.
Tested:
Unit Test -
```
> meson test -t 10 -C builddir/ --print-errorlogs --wrapper="valgrind --error-exitcode=1" test_firmware
ninja: Entering directory `/host/repos/Modbus/phosphor-modbus/builddir'
ninja: no work to do.
1/1 test_firmware OK 3.37s
Ok: 1
Fail: 0
```
Tested on Qemu -
```
> busctl tree xyz.openbmc_project.ModbusRTU
`- /xyz
`- /xyz/openbmc_project
|- /xyz/openbmc_project/inventory_source
| |- /xyz/openbmc_project/inventory_source/Heat_Exchanger_12_DevTTYUSB0
| |- /xyz/openbmc_project/inventory_source/Heat_Exchanger_12_DevTTYUSB1
| |- /xyz/openbmc_project/inventory_source/Reservoir_Pumping_Unit_12_DevTTYUSB0
| `- /xyz/openbmc_project/inventory_source/Reservoir_Pumping_Unit_12_DevTTYUSB1
|- /xyz/openbmc_project/sensors
| `- /xyz/openbmc_project/sensors/temperature
| |- /xyz/openbmc_project/sensors/temperature/Reservoir_Pumping_Unit_12_DevTTYUSB0_RPU_Coolant_Inlet_Temp_C
| |- /xyz/openbmc_project/sensors/temperature/Reservoir_Pumping_Unit_12_DevTTYUSB0_RPU_Coolant_Outlet_Temp_C
| |- /xyz/openbmc_project/sensors/temperature/Reservoir_Pumping_Unit_12_DevTTYUSB1_RPU_Coolant_Inlet_Temp_C
| `- /xyz/openbmc_project/sensors/temperature/Reservoir_Pumping_Unit_12_DevTTYUSB1_RPU_Coolant_Outlet_Temp_C
`- /xyz/openbmc_project/software
|- /xyz/openbmc_project/software/Reservoir_Pumping_Unit_12_DevTTYUSB0_RPU_PLC_FW_Revision_9071
`- /xyz/openbmc_project/software/Reservoir_Pumping_Unit_12_DevTTYUSB1_RPU_PLC_FW_Revision_8053
> busctl introspect xyz.openbmc_project.ModbusRTU /xyz/openbmc_project/software/Reservoir_Pumping_Unit_12_DevTTYUSB0_RPU_PLC_FW_Revision_9071
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -
xyz.openbmc_project.Association.Definitions interface - - -
.Associations property a(sss) 1 "running" "ran_on" "/xyz/openbmc_pr... emits-change writable
xyz.openbmc_project.Software.Activation interface - - -
.Activation property s "xyz.openbmc_project.Software.Activat... emits-change writable
.RequestedActivation property s "xyz.openbmc_project.Software.Activat... emits-change writable
xyz.openbmc_project.Software.Version interface - - -
.Purpose property s "xyz.openbmc_project.Software.Version... emits-change writable
.Version property s "ABABABAB" emits-change writable
```
Change-Id: I985e12ef88547585cca93569b083f347e74a8695
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/rtu/device/base_device.cpp b/rtu/device/base_device.cpp
index c075d55..80b6748 100644
--- a/rtu/device/base_device.cpp
+++ b/rtu/device/base_device.cpp
@@ -17,6 +17,13 @@
{
createSensors();
+ if (!config.firmwareRegisters.empty())
+ {
+ currentFirmware =
+ std::make_unique<DeviceFirmware>(ctx, config, serialPort);
+ ctx.spawn(currentFirmware->readVersionRegister());
+ }
+
info("Successfully created device {NAME}", "NAME", config.name);
}
diff --git a/rtu/device/base_device.hpp b/rtu/device/base_device.hpp
index 3a2f0a8..2310f1e 100644
--- a/rtu/device/base_device.hpp
+++ b/rtu/device/base_device.hpp
@@ -2,7 +2,7 @@
#include "base_config.hpp"
#include "common/events.hpp"
-#include "modbus/modbus.hpp"
+#include "firmware/device_firmware.hpp"
#include "port/base_port.hpp"
#include <sdbusplus/async.hpp>
@@ -45,6 +45,7 @@
const config::Config config;
PortIntf& serialPort;
EventIntf::Events& events;
+ std::unique_ptr<DeviceFirmware> currentFirmware;
sensors_map_t sensors;
};
diff --git a/rtu/firmware/device_firmware.cpp b/rtu/firmware/device_firmware.cpp
new file mode 100644
index 0000000..7626076
--- /dev/null
+++ b/rtu/firmware/device_firmware.cpp
@@ -0,0 +1,119 @@
+#include "device_firmware.hpp"
+
+#include "device/base_device.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+namespace phosphor::modbus::rtu::device
+{
+
+PHOSPHOR_LOG2_USING;
+
+static auto getRandomId() -> long int
+{
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ unsigned int seed = ts.tv_nsec ^ getpid();
+ srandom(seed);
+ return random() % 10000;
+}
+
+static auto getObjectPath(const config_intf::Config& config)
+ -> sdbusplus::message::object_path
+{
+ for (const auto& firmwareRegister : config.firmwareRegisters)
+ {
+ if (firmwareRegister.type == config_intf::FirmwareRegisterType::version)
+ {
+ if (firmwareRegister.name.empty())
+ {
+ return sdbusplus::message::object_path(
+ FirmwareIntf::namespace_path) /
+ std::format("{}_{}", config.name, getRandomId());
+ }
+ else
+ {
+ return sdbusplus::message::object_path(
+ FirmwareIntf::namespace_path) /
+ std::format("{}_{}_{}", config.name,
+ firmwareRegister.name, getRandomId());
+ }
+ }
+ }
+
+ throw std::runtime_error(
+ "No firmware version register found for " + config.name);
+}
+
+constexpr FirmwareIntf::Version::properties_t initVersion{
+ "Unknown", FirmwareIntf::VersionPurpose::Other};
+constexpr FirmwareIntf::Activation::properties_t initActivation{
+ FirmwareIntf::Activations::NotReady,
+ FirmwareIntf::RequestedActivations::None};
+constexpr FirmwareIntf::Definitions::properties_t initAssociations{};
+
+DeviceFirmware::DeviceFirmware(sdbusplus::async::context& ctx,
+ const config_intf::Config& config,
+ PortIntf& serialPort) :
+ objectPath(getObjectPath(config)),
+ currentFirmware(
+ std::make_unique<FirmwareIntf>(ctx, objectPath.str.c_str(), initVersion,
+ initActivation, initAssociations)),
+ config(config), serialPort(serialPort)
+{
+ currentFirmware->Version::emit_added();
+ currentFirmware->Activation::emit_added();
+ currentFirmware->Definitions::emit_added();
+
+ info("Device firmware {NAME} created successfully", "NAME", config.name);
+}
+
+auto DeviceFirmware::readVersionRegister() -> sdbusplus::async::task<void>
+{
+ const auto it = std::find_if(
+ config.firmwareRegisters.begin(), config.firmwareRegisters.end(),
+ [](const config_intf::FirmwareRegister& firmwareRegister) {
+ return firmwareRegister.type ==
+ config_intf::FirmwareRegisterType::version;
+ });
+
+ if (it == config.firmwareRegisters.end())
+ {
+ error("No firmware version register found for {NAME}", "NAME",
+ config.name);
+ co_return;
+ }
+
+ const config_intf::FirmwareRegister& versionRegister = *it;
+
+ auto registers = std::vector<uint16_t>(versionRegister.size);
+ auto ret = co_await serialPort.readHoldingRegisters(
+ config.address, versionRegister.offset, config.baudRate, config.parity,
+ registers);
+ if (!ret)
+ {
+ error("Failed to read holding registers {NAME} for {DEVICE_ADDRESS}",
+ "NAME", versionRegister.name, "DEVICE_ADDRESS", config.address);
+ co_return;
+ }
+
+ std::string strValue = "";
+
+ for (const auto& value : registers)
+ {
+ strValue += static_cast<char>((value >> 8) & 0xFF);
+ strValue += static_cast<char>(value & 0xFF);
+ }
+
+ currentFirmware->version(strValue);
+ currentFirmware->activation(FirmwareIntf::Activation::Activations::Active);
+ auto associationList =
+ std::vector<std::tuple<std::string, std::string, std::string>>{
+ {"running", "ran_on", config.inventoryPath}};
+ currentFirmware->associations(associationList);
+
+ info("Firmware version {VERSION} for {NAME} at {DEVICE_ADDRESS}", "VERSION",
+ strValue, "NAME", config.name, "DEVICE_ADDRESS", config.address);
+}
+
+} // namespace phosphor::modbus::rtu::device
diff --git a/rtu/firmware/device_firmware.hpp b/rtu/firmware/device_firmware.hpp
new file mode 100644
index 0000000..c3839f4
--- /dev/null
+++ b/rtu/firmware/device_firmware.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "device/base_config.hpp"
+#include "port/base_port.hpp"
+
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/async/server.hpp>
+#include <xyz/openbmc_project/Association/Definitions/aserver.hpp>
+#include <xyz/openbmc_project/Software/Activation/aserver.hpp>
+#include <xyz/openbmc_project/Software/Version/aserver.hpp>
+
+namespace phosphor::modbus::rtu::device
+{
+
+namespace config
+{
+
+struct Config;
+
+} // namespace config
+
+namespace config_intf = phosphor::modbus::rtu::device::config;
+using PortIntf = phosphor::modbus::rtu::port::BasePort;
+
+class DeviceFirmware;
+
+using FirmwareIntf = sdbusplus::async::server_t<
+ DeviceFirmware, sdbusplus::aserver::xyz::openbmc_project::software::Version,
+ sdbusplus::aserver::xyz::openbmc_project::software::Activation,
+ sdbusplus::aserver::xyz::openbmc_project::association::Definitions>;
+
+class DeviceFirmware
+{
+ public:
+ DeviceFirmware() = delete;
+
+ explicit DeviceFirmware(sdbusplus::async::context& ctx,
+ const config_intf::Config& config,
+ PortIntf& serialPort);
+
+ auto readVersionRegister() -> sdbusplus::async::task<void>;
+
+ protected:
+ // Object path of current firmware object
+ // TODO: check if its possible to get rid off this via mocking since its
+ // only used in tests
+ const sdbusplus::message::object_path objectPath;
+
+ private:
+ std::unique_ptr<FirmwareIntf> currentFirmware;
+ const config_intf::Config config;
+ PortIntf& serialPort;
+};
+
+} // namespace phosphor::modbus::rtu::device
diff --git a/rtu/meson.build b/rtu/meson.build
index a8b9101..e95c7bc 100644
--- a/rtu/meson.build
+++ b/rtu/meson.build
@@ -32,9 +32,11 @@
'device/reservoir_pump_unit.cpp',
)
+firmware_src = files('firmware/device_firmware.cpp')
+
executable(
'phosphor-modbus-rtu',
- ['device_manager.cpp', inventory_src, device_src],
+ ['device_manager.cpp', inventory_src, device_src, firmware_src],
include_directories: ['.', common_include],
dependencies: [default_deps],
link_with: [modbus_common_lib, modbus_rtu_lib, modbus_rtu_port_lib],