xref: /openbmc/phosphor-modbus/rtu/inventory/modbus_inventory.cpp (revision e92aba4516471f5a01d4ab1f93eb9919ec05c21f)
1cad9ecf6SJagpal Singh Gill #include "modbus_inventory.hpp"
2cad9ecf6SJagpal Singh Gill 
3cad9ecf6SJagpal Singh Gill #include "common/entity_manager_interface.hpp"
4cad9ecf6SJagpal Singh Gill 
5cad9ecf6SJagpal Singh Gill #include <phosphor-logging/lg2.hpp>
6cad9ecf6SJagpal Singh Gill #include <xyz/openbmc_project/Configuration/ModbusRTUDetect/client.hpp>
7cad9ecf6SJagpal Singh Gill #include <xyz/openbmc_project/Inventory/Item/client.hpp>
8cad9ecf6SJagpal Singh Gill 
9cad9ecf6SJagpal Singh Gill #include <flat_map>
10cad9ecf6SJagpal Singh Gill 
11cad9ecf6SJagpal Singh Gill namespace phosphor::modbus::rtu::inventory
12cad9ecf6SJagpal Singh Gill {
13cad9ecf6SJagpal Singh Gill PHOSPHOR_LOG2_USING;
14cad9ecf6SJagpal Singh Gill 
15cad9ecf6SJagpal Singh Gill namespace config
16cad9ecf6SJagpal Singh Gill {
17cad9ecf6SJagpal Singh Gill 
18cad9ecf6SJagpal Singh Gill using BasicVariantType =
19cad9ecf6SJagpal Singh Gill     std::variant<std::vector<std::string>, std::vector<uint8_t>, std::string,
20cad9ecf6SJagpal Singh Gill                  int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
21cad9ecf6SJagpal Singh Gill                  uint16_t, uint8_t, bool>;
22cad9ecf6SJagpal Singh Gill using InventoryBaseConfigMap = std::flat_map<std::string, BasicVariantType>;
23cad9ecf6SJagpal Singh Gill using InventoryData = std::flat_map<std::string, InventoryBaseConfigMap>;
24cad9ecf6SJagpal Singh Gill using ManagedObjectType =
25cad9ecf6SJagpal Singh Gill     std::flat_map<sdbusplus::message::object_path, InventoryData>;
26cad9ecf6SJagpal Singh Gill 
27cad9ecf6SJagpal Singh Gill static constexpr std::array<std::pair<std::string_view, Parity>, 3>
28cad9ecf6SJagpal Singh Gill     validParities = {
29cad9ecf6SJagpal Singh Gill         {{"Odd", Parity::odd}, {"Even", Parity::even}, {"None", Parity::none}}};
30cad9ecf6SJagpal Singh Gill 
31cad9ecf6SJagpal Singh Gill // TODO: This API will be dropped once EM supports non-indexed interfaces for
32cad9ecf6SJagpal Singh Gill // array objects.
processModbusAddressInterface(Config & config,const InventoryBaseConfigMap & configMap)33cad9ecf6SJagpal Singh Gill static auto processModbusAddressInterface(
34cad9ecf6SJagpal Singh Gill     Config& config, const InventoryBaseConfigMap& configMap) -> bool
35cad9ecf6SJagpal Singh Gill {
36cad9ecf6SJagpal Singh Gill     debug("Processing ModbusAddress {NAME}", "NAME", config.name);
37cad9ecf6SJagpal Singh Gill 
38cad9ecf6SJagpal Singh Gill     auto rangeStartIter = configMap.find("RangeStart");
39cad9ecf6SJagpal Singh Gill     if (rangeStartIter == configMap.end())
40cad9ecf6SJagpal Singh Gill     {
41cad9ecf6SJagpal Singh Gill         error("Missing RangeStart for {NAME}", "NAME", config.name);
42cad9ecf6SJagpal Singh Gill         return false;
43cad9ecf6SJagpal Singh Gill     }
44cad9ecf6SJagpal Singh Gill     auto rangeStart = std::get<uint64_t>(rangeStartIter->second);
45cad9ecf6SJagpal Singh Gill 
46cad9ecf6SJagpal Singh Gill     auto rangeEndIter = configMap.find("RangeEnd");
47cad9ecf6SJagpal Singh Gill     if (rangeEndIter == configMap.end())
48cad9ecf6SJagpal Singh Gill     {
49cad9ecf6SJagpal Singh Gill         error("Missing RangeEnd for {NAME}", "NAME", config.name);
50cad9ecf6SJagpal Singh Gill         return false;
51cad9ecf6SJagpal Singh Gill     }
52cad9ecf6SJagpal Singh Gill     auto rangeEnd = std::get<uint64_t>(rangeEndIter->second);
53cad9ecf6SJagpal Singh Gill 
54cad9ecf6SJagpal Singh Gill     auto serialPortIter = configMap.find("SerialPort");
55cad9ecf6SJagpal Singh Gill     if (serialPortIter == configMap.end())
56cad9ecf6SJagpal Singh Gill     {
57cad9ecf6SJagpal Singh Gill         error("Missing SerialPort for {NAME}", "NAME", config.name);
58cad9ecf6SJagpal Singh Gill         return false;
59cad9ecf6SJagpal Singh Gill     }
60cad9ecf6SJagpal Singh Gill     auto serialPort = std::get<std::string>(serialPortIter->second);
61cad9ecf6SJagpal Singh Gill 
62cad9ecf6SJagpal Singh Gill     config.addressMap[serialPort].push_back(AddressRange{
63cad9ecf6SJagpal Singh Gill         static_cast<uint8_t>(rangeStart), static_cast<uint8_t>(rangeEnd)});
64cad9ecf6SJagpal Singh Gill 
65cad9ecf6SJagpal Singh Gill     debug("ModbusAddress {NAME} {PORT} {START} {END}", "NAME", config.name,
66cad9ecf6SJagpal Singh Gill           "PORT", serialPort, "START", rangeStart, "END", rangeEnd);
67cad9ecf6SJagpal Singh Gill 
68cad9ecf6SJagpal Singh Gill     return true;
69cad9ecf6SJagpal Singh Gill }
70cad9ecf6SJagpal Singh Gill 
71cad9ecf6SJagpal Singh Gill // TODO: This API will be dropped once EM supports non-indexed interfaces for
72cad9ecf6SJagpal Singh Gill // array objects.
processModbusRegistersInterface(Config & config,const InventoryBaseConfigMap & configMap)73cad9ecf6SJagpal Singh Gill static auto processModbusRegistersInterface(
74cad9ecf6SJagpal Singh Gill     Config& config, const InventoryBaseConfigMap& configMap) -> bool
75cad9ecf6SJagpal Singh Gill {
76cad9ecf6SJagpal Singh Gill     debug("Processing ModbusRegisters {NAME}", "NAME", config.name);
77cad9ecf6SJagpal Singh Gill     Register registerConfig = {};
78cad9ecf6SJagpal Singh Gill 
79cad9ecf6SJagpal Singh Gill     auto nameIter = configMap.find("Name");
80cad9ecf6SJagpal Singh Gill     if (nameIter == configMap.end())
81cad9ecf6SJagpal Singh Gill     {
82cad9ecf6SJagpal Singh Gill         error("Missing Name for {NAME}", "NAME", config.name);
83cad9ecf6SJagpal Singh Gill         return false;
84cad9ecf6SJagpal Singh Gill     }
85cad9ecf6SJagpal Singh Gill     registerConfig.name = std::get<std::string>(nameIter->second);
86cad9ecf6SJagpal Singh Gill 
87cad9ecf6SJagpal Singh Gill     auto address = configMap.find("Address");
88cad9ecf6SJagpal Singh Gill     if (address == configMap.end())
89cad9ecf6SJagpal Singh Gill     {
90cad9ecf6SJagpal Singh Gill         error("Missing Address for {NAME}", "NAME", config.name);
91cad9ecf6SJagpal Singh Gill         return false;
92cad9ecf6SJagpal Singh Gill     }
93cad9ecf6SJagpal Singh Gill     registerConfig.offset = std::get<uint64_t>(address->second);
94cad9ecf6SJagpal Singh Gill 
95cad9ecf6SJagpal Singh Gill     auto sizeIter = configMap.find("Size");
96cad9ecf6SJagpal Singh Gill     if (sizeIter == configMap.end())
97cad9ecf6SJagpal Singh Gill     {
98cad9ecf6SJagpal Singh Gill         error("Missing Size for {NAME}", "NAME", config.name);
99cad9ecf6SJagpal Singh Gill         return false;
100cad9ecf6SJagpal Singh Gill     }
101cad9ecf6SJagpal Singh Gill     registerConfig.size = std::get<uint64_t>(sizeIter->second);
102cad9ecf6SJagpal Singh Gill 
103cad9ecf6SJagpal Singh Gill     config.registers.push_back(registerConfig);
104cad9ecf6SJagpal Singh Gill 
105cad9ecf6SJagpal Singh Gill     debug("ModbusRegisters {NAME} {ADDRESS} {SIZE}", "NAME",
106cad9ecf6SJagpal Singh Gill           registerConfig.name, "ADDRESS", registerConfig.offset, "SIZE",
107cad9ecf6SJagpal Singh Gill           registerConfig.size);
108cad9ecf6SJagpal Singh Gill 
109cad9ecf6SJagpal Singh Gill     return true;
110cad9ecf6SJagpal Singh Gill }
111cad9ecf6SJagpal Singh Gill 
printConfig(const Config & config)112cad9ecf6SJagpal Singh Gill static auto printConfig(const Config& config) -> void
113cad9ecf6SJagpal Singh Gill {
114cad9ecf6SJagpal Singh Gill     info("Inventory device config: {NAME} {BAUDRATE} {PARITY}", "NAME",
115cad9ecf6SJagpal Singh Gill          config.name, "BAUDRATE", config.baudRate, "PARITY", config.parity);
116cad9ecf6SJagpal Singh Gill 
117cad9ecf6SJagpal Singh Gill     for (const auto& [port, addressRanges] : config.addressMap)
118cad9ecf6SJagpal Singh Gill     {
119cad9ecf6SJagpal Singh Gill         for (const auto& addressRange : addressRanges)
120cad9ecf6SJagpal Singh Gill         {
121cad9ecf6SJagpal Singh Gill             info(
122cad9ecf6SJagpal Singh Gill                 "Inventory device config: {PORT} {ADDRESS_START} {ADDRESS_END}",
123cad9ecf6SJagpal Singh Gill                 "PORT", port, "ADDRESS_START", addressRange.start,
124cad9ecf6SJagpal Singh Gill                 "ADDRESS_END", addressRange.end);
125cad9ecf6SJagpal Singh Gill         }
126cad9ecf6SJagpal Singh Gill     }
127cad9ecf6SJagpal Singh Gill 
128cad9ecf6SJagpal Singh Gill     for (const auto& registerConfig : config.registers)
129cad9ecf6SJagpal Singh Gill     {
130cad9ecf6SJagpal Singh Gill         info("Inventory device config: {NAME} {ADDRESS} {SIZE}", "NAME",
131cad9ecf6SJagpal Singh Gill              registerConfig.name, "ADDRESS", registerConfig.offset, "SIZE",
132cad9ecf6SJagpal Singh Gill              registerConfig.size);
133cad9ecf6SJagpal Singh Gill     }
134cad9ecf6SJagpal Singh Gill }
135cad9ecf6SJagpal Singh Gill 
getConfigSubInterfaces(sdbusplus::async::context & ctx,sdbusplus::message::object_path objectPath,Config & config)136cad9ecf6SJagpal Singh Gill auto getConfigSubInterfaces(sdbusplus::async::context& ctx,
137cad9ecf6SJagpal Singh Gill                             sdbusplus::message::object_path objectPath,
138cad9ecf6SJagpal Singh Gill                             Config& config) -> sdbusplus::async::task<bool>
139cad9ecf6SJagpal Singh Gill {
140cad9ecf6SJagpal Singh Gill     constexpr auto modbusAddressInterface =
141cad9ecf6SJagpal Singh Gill         "xyz.openbmc_project.Configuration.ModbusRTUDetect.Address";
142cad9ecf6SJagpal Singh Gill     constexpr auto modbusRegistersInterface =
143cad9ecf6SJagpal Singh Gill         "xyz.openbmc_project.Configuration.ModbusRTUDetect.Registers";
144cad9ecf6SJagpal Singh Gill 
145cad9ecf6SJagpal Singh Gill     using InventoryIntf =
146cad9ecf6SJagpal Singh Gill         sdbusplus::client::xyz::openbmc_project::inventory::Item<>;
147cad9ecf6SJagpal Singh Gill 
148cad9ecf6SJagpal Singh Gill     constexpr auto entityManager =
149cad9ecf6SJagpal Singh Gill         sdbusplus::async::proxy()
150cad9ecf6SJagpal Singh Gill             .service(entity_manager::EntityManagerInterface::serviceName)
151cad9ecf6SJagpal Singh Gill             .path(InventoryIntf::namespace_path)
152cad9ecf6SJagpal Singh Gill             .interface("org.freedesktop.DBus.ObjectManager");
153cad9ecf6SJagpal Singh Gill 
154cad9ecf6SJagpal Singh Gill     for (const auto& [path, deviceConfig] :
155cad9ecf6SJagpal Singh Gill          co_await entityManager.call<ManagedObjectType>(ctx,
156cad9ecf6SJagpal Singh Gill                                                         "GetManagedObjects"))
157cad9ecf6SJagpal Singh Gill     {
158cad9ecf6SJagpal Singh Gill         if (!(path.str).starts_with(objectPath.str))
159cad9ecf6SJagpal Singh Gill         {
160cad9ecf6SJagpal Singh Gill             debug("Skipping device {PATH}", "PATH", path.str);
161cad9ecf6SJagpal Singh Gill             continue;
162cad9ecf6SJagpal Singh Gill         }
163cad9ecf6SJagpal Singh Gill         debug("Processing device {PATH}", "PATH", path.str);
164cad9ecf6SJagpal Singh Gill         for (const auto& [interfaceName, interfaceConfig] : deviceConfig)
165cad9ecf6SJagpal Singh Gill         {
166cad9ecf6SJagpal Singh Gill             if (interfaceName.starts_with(modbusAddressInterface))
167cad9ecf6SJagpal Singh Gill             {
168cad9ecf6SJagpal Singh Gill                 if (!processModbusAddressInterface(config, interfaceConfig))
169cad9ecf6SJagpal Singh Gill                 {
170cad9ecf6SJagpal Singh Gill                     error("Failed to process {INTERFACE} for {NAME}",
171cad9ecf6SJagpal Singh Gill                           "INTERFACE", modbusAddressInterface, "NAME",
172cad9ecf6SJagpal Singh Gill                           config.name);
173cad9ecf6SJagpal Singh Gill                     co_return false;
174cad9ecf6SJagpal Singh Gill                 }
175cad9ecf6SJagpal Singh Gill             }
176cad9ecf6SJagpal Singh Gill             else if (interfaceName.starts_with(modbusRegistersInterface))
177cad9ecf6SJagpal Singh Gill             {
178cad9ecf6SJagpal Singh Gill                 if (!processModbusRegistersInterface(config, interfaceConfig))
179cad9ecf6SJagpal Singh Gill                 {
180cad9ecf6SJagpal Singh Gill                     error("Failed to process {INTERFACE} for {NAME}",
181cad9ecf6SJagpal Singh Gill                           "INTERFACE", modbusRegistersInterface, "NAME",
182cad9ecf6SJagpal Singh Gill                           config.name);
183cad9ecf6SJagpal Singh Gill                     co_return false;
184cad9ecf6SJagpal Singh Gill                 }
185cad9ecf6SJagpal Singh Gill             }
186cad9ecf6SJagpal Singh Gill         }
187cad9ecf6SJagpal Singh Gill     }
188cad9ecf6SJagpal Singh Gill 
189cad9ecf6SJagpal Singh Gill     co_return true;
190cad9ecf6SJagpal Singh Gill }
191cad9ecf6SJagpal Singh Gill 
getConfig(sdbusplus::async::context & ctx,sdbusplus::message::object_path objectPath)192cad9ecf6SJagpal Singh Gill auto getConfig(sdbusplus::async::context& ctx,
193cad9ecf6SJagpal Singh Gill                sdbusplus::message::object_path objectPath)
194cad9ecf6SJagpal Singh Gill     -> sdbusplus::async::task<std::optional<Config>>
195cad9ecf6SJagpal Singh Gill {
196cad9ecf6SJagpal Singh Gill     using ModbusRTUDetectIntf = sdbusplus::client::xyz::openbmc_project::
197cad9ecf6SJagpal Singh Gill         configuration::ModbusRTUDetect<>;
198cad9ecf6SJagpal Singh Gill 
199cad9ecf6SJagpal Singh Gill     Config config = {};
200cad9ecf6SJagpal Singh Gill 
201cad9ecf6SJagpal Singh Gill     auto properties =
202cad9ecf6SJagpal Singh Gill         co_await ModbusRTUDetectIntf(ctx)
203cad9ecf6SJagpal Singh Gill             .service(entity_manager::EntityManagerInterface::serviceName)
204cad9ecf6SJagpal Singh Gill             .path(objectPath.str)
205cad9ecf6SJagpal Singh Gill             .properties();
206cad9ecf6SJagpal Singh Gill 
207cad9ecf6SJagpal Singh Gill     config.name = properties.name;
208cad9ecf6SJagpal Singh Gill     config.baudRate = properties.baud_rate;
209cad9ecf6SJagpal Singh Gill 
210cad9ecf6SJagpal Singh Gill     for (const auto& [parityStr, parity] : config::validParities)
211cad9ecf6SJagpal Singh Gill     {
212cad9ecf6SJagpal Singh Gill         if (parityStr == properties.data_parity)
213cad9ecf6SJagpal Singh Gill         {
214cad9ecf6SJagpal Singh Gill             config.parity = parity;
215cad9ecf6SJagpal Singh Gill             break;
216cad9ecf6SJagpal Singh Gill         }
217cad9ecf6SJagpal Singh Gill     }
218cad9ecf6SJagpal Singh Gill     if (config.parity == Parity::unknown)
219cad9ecf6SJagpal Singh Gill     {
220cad9ecf6SJagpal Singh Gill         error("Invalid parity {PARITY} for {NAME}", "PARITY",
221cad9ecf6SJagpal Singh Gill               properties.data_parity, "NAME", properties.name);
222cad9ecf6SJagpal Singh Gill         co_return std::nullopt;
223cad9ecf6SJagpal Singh Gill     }
224cad9ecf6SJagpal Singh Gill 
225cad9ecf6SJagpal Singh Gill     if (!co_await getConfigSubInterfaces(ctx, objectPath, config))
226cad9ecf6SJagpal Singh Gill     {
227cad9ecf6SJagpal Singh Gill         co_return std::nullopt;
228cad9ecf6SJagpal Singh Gill     }
229cad9ecf6SJagpal Singh Gill 
230cad9ecf6SJagpal Singh Gill     printConfig(config);
231cad9ecf6SJagpal Singh Gill 
232cad9ecf6SJagpal Singh Gill     co_return config;
233cad9ecf6SJagpal Singh Gill }
234cad9ecf6SJagpal Singh Gill 
235cad9ecf6SJagpal Singh Gill } // namespace config
236cad9ecf6SJagpal Singh Gill 
Device(sdbusplus::async::context & ctx,const config::Config & config,serial_port_map_t & serialPorts)237cad9ecf6SJagpal Singh Gill Device::Device(sdbusplus::async::context& ctx, const config::Config& config,
238cad9ecf6SJagpal Singh Gill                serial_port_map_t& serialPorts) :
239*e92aba45SJagpal Singh Gill     config(config), ctx(ctx), serialPorts(serialPorts)
240cad9ecf6SJagpal Singh Gill {
241cad9ecf6SJagpal Singh Gill     for (const auto& [serialPort, _] : config.addressMap)
242cad9ecf6SJagpal Singh Gill     {
243cad9ecf6SJagpal Singh Gill         if (serialPorts.find(serialPort) == serialPorts.end())
244cad9ecf6SJagpal Singh Gill         {
245cad9ecf6SJagpal Singh Gill             error("Serial port {PORT} not found for {NAME}", "PORT", serialPort,
246cad9ecf6SJagpal Singh Gill                   "NAME", config.name);
247cad9ecf6SJagpal Singh Gill             continue;
248cad9ecf6SJagpal Singh Gill         }
249cad9ecf6SJagpal Singh Gill     }
250cad9ecf6SJagpal Singh Gill }
251cad9ecf6SJagpal Singh Gill 
probePorts()252cad9ecf6SJagpal Singh Gill auto Device::probePorts() -> sdbusplus::async::task<void>
253cad9ecf6SJagpal Singh Gill {
254cad9ecf6SJagpal Singh Gill     debug("Probing ports for {NAME}", "NAME", config.name);
255cad9ecf6SJagpal Singh Gill     while (!ctx.stop_requested())
256cad9ecf6SJagpal Singh Gill     {
257cad9ecf6SJagpal Singh Gill         for (const auto& [serialPort, _] : config.addressMap)
258cad9ecf6SJagpal Singh Gill         {
259cad9ecf6SJagpal Singh Gill             if (serialPorts.find(serialPort) == serialPorts.end())
260cad9ecf6SJagpal Singh Gill             {
261cad9ecf6SJagpal Singh Gill                 continue;
262cad9ecf6SJagpal Singh Gill             }
263cad9ecf6SJagpal Singh Gill             ctx.spawn(probePort(serialPort));
264cad9ecf6SJagpal Singh Gill         }
265cad9ecf6SJagpal Singh Gill         constexpr auto probeInterval = 3;
266cad9ecf6SJagpal Singh Gill         co_await sdbusplus::async::sleep_for(
267cad9ecf6SJagpal Singh Gill             ctx, std::chrono::seconds(probeInterval));
268cad9ecf6SJagpal Singh Gill         debug("Probing ports for {NAME} in {INTERVAL} seconds", "NAME",
269cad9ecf6SJagpal Singh Gill               config.name, "INTERVAL", probeInterval);
270cad9ecf6SJagpal Singh Gill     }
271cad9ecf6SJagpal Singh Gill }
272cad9ecf6SJagpal Singh Gill 
probePort(std::string portName)273cad9ecf6SJagpal Singh Gill auto Device::probePort(std::string portName) -> sdbusplus::async::task<void>
274cad9ecf6SJagpal Singh Gill {
275cad9ecf6SJagpal Singh Gill     debug("Probing port {PORT}", "PORT", portName);
276cad9ecf6SJagpal Singh Gill 
277cad9ecf6SJagpal Singh Gill     auto portConfig = config.addressMap.find(portName);
278cad9ecf6SJagpal Singh Gill     if (portConfig == config.addressMap.end())
279cad9ecf6SJagpal Singh Gill     {
280cad9ecf6SJagpal Singh Gill         error("Serial port {PORT} address map not found for {NAME}", "PORT",
281cad9ecf6SJagpal Singh Gill               portName, "NAME", config.name);
282cad9ecf6SJagpal Singh Gill         co_return;
283cad9ecf6SJagpal Singh Gill     }
284cad9ecf6SJagpal Singh Gill     auto addressRanges = portConfig->second;
285cad9ecf6SJagpal Singh Gill 
286cad9ecf6SJagpal Singh Gill     auto port = serialPorts.find(portName);
287cad9ecf6SJagpal Singh Gill     if (port == serialPorts.end())
288cad9ecf6SJagpal Singh Gill     {
289cad9ecf6SJagpal Singh Gill         error("Serial port {PORT} not found for {NAME}", "PORT", portName,
290cad9ecf6SJagpal Singh Gill               "NAME", config.name);
291cad9ecf6SJagpal Singh Gill         co_return;
292cad9ecf6SJagpal Singh Gill     }
293cad9ecf6SJagpal Singh Gill 
294cad9ecf6SJagpal Singh Gill     for (const auto& addressRange : addressRanges)
295cad9ecf6SJagpal Singh Gill     {
296cad9ecf6SJagpal Singh Gill         for (auto address = addressRange.start; address <= addressRange.end;
297cad9ecf6SJagpal Singh Gill              address++)
298cad9ecf6SJagpal Singh Gill         {
299cad9ecf6SJagpal Singh Gill             co_await probeDevice(address, portName, *port->second);
300cad9ecf6SJagpal Singh Gill         }
301cad9ecf6SJagpal Singh Gill     }
302cad9ecf6SJagpal Singh Gill }
303cad9ecf6SJagpal Singh Gill 
probeDevice(uint8_t address,const std::string & portName,SerialPortIntf & port)304cad9ecf6SJagpal Singh Gill auto Device::probeDevice(uint8_t address, const std::string& portName,
305cad9ecf6SJagpal Singh Gill                          SerialPortIntf& port) -> sdbusplus::async::task<void>
306cad9ecf6SJagpal Singh Gill {
307cad9ecf6SJagpal Singh Gill     debug("Probing device at {ADDRESS} on port {PORT}", "ADDRESS", address,
308cad9ecf6SJagpal Singh Gill           "PORT", portName);
309cad9ecf6SJagpal Singh Gill 
310cad9ecf6SJagpal Singh Gill     if (config.registers.size() == 0)
311cad9ecf6SJagpal Singh Gill     {
312cad9ecf6SJagpal Singh Gill         error("No registers configured for {NAME}", "NAME", config.name);
313cad9ecf6SJagpal Singh Gill         co_return;
314cad9ecf6SJagpal Singh Gill     }
315cad9ecf6SJagpal Singh Gill     auto probeRegister = config.registers[0].offset;
316cad9ecf6SJagpal Singh Gill     auto registers = std::vector<uint16_t>(config.registers[0].size);
317cad9ecf6SJagpal Singh Gill 
318cad9ecf6SJagpal Singh Gill     auto sourceId = std::to_string(address) + "_" + portName;
319cad9ecf6SJagpal Singh Gill 
320cad9ecf6SJagpal Singh Gill     auto ret = co_await port.readHoldingRegisters(
321cad9ecf6SJagpal Singh Gill         address, probeRegister, config.baudRate, config.parity, registers);
322cad9ecf6SJagpal Singh Gill     if (ret)
323cad9ecf6SJagpal Singh Gill     {
324cad9ecf6SJagpal Singh Gill         if (inventorySources.find(sourceId) == inventorySources.end())
325cad9ecf6SJagpal Singh Gill         {
326cad9ecf6SJagpal Singh Gill             debug("Device found at {ADDRESS}", "ADDRESS", address);
327cad9ecf6SJagpal Singh Gill             co_await addInventorySource(address, portName, port);
328cad9ecf6SJagpal Singh Gill         }
329cad9ecf6SJagpal Singh Gill         else
330cad9ecf6SJagpal Singh Gill         {
331cad9ecf6SJagpal Singh Gill             debug("Device already exists at {ADDRESS}", "ADDRESS", address);
332cad9ecf6SJagpal Singh Gill         }
333cad9ecf6SJagpal Singh Gill     }
334cad9ecf6SJagpal Singh Gill     else
335cad9ecf6SJagpal Singh Gill     {
336cad9ecf6SJagpal Singh Gill         if (inventorySources.find(sourceId) != inventorySources.end())
337cad9ecf6SJagpal Singh Gill         {
338cad9ecf6SJagpal Singh Gill             warning(
339cad9ecf6SJagpal Singh Gill                 "Device removed at {ADDRESS} due to probe failure for {PROBE_REGISTER}",
340cad9ecf6SJagpal Singh Gill                 "ADDRESS", address, "PROBE_REGISTER", probeRegister);
341cad9ecf6SJagpal Singh Gill             inventorySources[sourceId]->emit_removed();
342cad9ecf6SJagpal Singh Gill             inventorySources.erase(sourceId);
343cad9ecf6SJagpal Singh Gill         }
344cad9ecf6SJagpal Singh Gill     }
345cad9ecf6SJagpal Singh Gill }
346cad9ecf6SJagpal Singh Gill 
fillInventorySourceProperties(InventorySourceIntf::properties_t & properties,const std::string & regName,std::string & strValue)347cad9ecf6SJagpal Singh Gill static auto fillInventorySourceProperties(
348cad9ecf6SJagpal Singh Gill     InventorySourceIntf::properties_t& properties, const std::string& regName,
349cad9ecf6SJagpal Singh Gill     std::string& strValue) -> void
350cad9ecf6SJagpal Singh Gill {
351cad9ecf6SJagpal Singh Gill     constexpr auto partNumber = "PartNumber";
352cad9ecf6SJagpal Singh Gill     constexpr auto sparePartNumber = "SparePartNumber";
353cad9ecf6SJagpal Singh Gill     constexpr auto serialNumber = "SerialNumber";
354cad9ecf6SJagpal Singh Gill     constexpr auto buildDate = "BuildDate";
355cad9ecf6SJagpal Singh Gill     constexpr auto model = "Model";
356cad9ecf6SJagpal Singh Gill     constexpr auto manufacturer = "Manufacturer";
357cad9ecf6SJagpal Singh Gill 
358cad9ecf6SJagpal Singh Gill     if (regName == partNumber)
359cad9ecf6SJagpal Singh Gill     {
360cad9ecf6SJagpal Singh Gill         properties.part_number = strValue;
361cad9ecf6SJagpal Singh Gill     }
362cad9ecf6SJagpal Singh Gill     else if (regName == sparePartNumber)
363cad9ecf6SJagpal Singh Gill     {
364cad9ecf6SJagpal Singh Gill         properties.spare_part_number = strValue;
365cad9ecf6SJagpal Singh Gill     }
366cad9ecf6SJagpal Singh Gill     else if (regName == serialNumber)
367cad9ecf6SJagpal Singh Gill     {
368cad9ecf6SJagpal Singh Gill         properties.serial_number = strValue;
369cad9ecf6SJagpal Singh Gill     }
370cad9ecf6SJagpal Singh Gill     else if (regName == buildDate)
371cad9ecf6SJagpal Singh Gill     {
372cad9ecf6SJagpal Singh Gill         properties.build_date = strValue;
373cad9ecf6SJagpal Singh Gill     }
374cad9ecf6SJagpal Singh Gill     else if (regName == model)
375cad9ecf6SJagpal Singh Gill     {
376cad9ecf6SJagpal Singh Gill         properties.model = strValue;
377cad9ecf6SJagpal Singh Gill     }
378cad9ecf6SJagpal Singh Gill     else if (regName == manufacturer)
379cad9ecf6SJagpal Singh Gill     {
380cad9ecf6SJagpal Singh Gill         properties.manufacturer = strValue;
381cad9ecf6SJagpal Singh Gill     }
382cad9ecf6SJagpal Singh Gill }
383cad9ecf6SJagpal Singh Gill 
addInventorySource(uint8_t address,const std::string & portName,SerialPortIntf & port)384cad9ecf6SJagpal Singh Gill auto Device::addInventorySource(uint8_t address, const std::string& portName,
385cad9ecf6SJagpal Singh Gill                                 SerialPortIntf& port)
386cad9ecf6SJagpal Singh Gill     -> sdbusplus::async::task<void>
387cad9ecf6SJagpal Singh Gill {
388cad9ecf6SJagpal Singh Gill     InventorySourceIntf::properties_t properties;
389cad9ecf6SJagpal Singh Gill 
390cad9ecf6SJagpal Singh Gill     for (const auto& reg : config.registers)
391cad9ecf6SJagpal Singh Gill     {
392cad9ecf6SJagpal Singh Gill         auto registers = std::vector<uint16_t>(reg.size);
393cad9ecf6SJagpal Singh Gill         auto ret = co_await port.readHoldingRegisters(
394cad9ecf6SJagpal Singh Gill             address, reg.offset, config.baudRate, config.parity, registers);
395cad9ecf6SJagpal Singh Gill         if (!ret)
396cad9ecf6SJagpal Singh Gill         {
397cad9ecf6SJagpal Singh Gill             error(
398cad9ecf6SJagpal Singh Gill                 "Failed to read holding registers {NAME} for {DEVICE_ADDRESS}",
399cad9ecf6SJagpal Singh Gill                 "NAME", reg.name, "DEVICE_ADDRESS", address);
400cad9ecf6SJagpal Singh Gill             continue;
401cad9ecf6SJagpal Singh Gill         }
402cad9ecf6SJagpal Singh Gill 
403cad9ecf6SJagpal Singh Gill         std::string strValue = "";
404cad9ecf6SJagpal Singh Gill 
405cad9ecf6SJagpal Singh Gill         // Reswap bytes in each register for string conversion
406cad9ecf6SJagpal Singh Gill         for (const auto& value : registers)
407cad9ecf6SJagpal Singh Gill         {
408cad9ecf6SJagpal Singh Gill             strValue += static_cast<char>((value >> 8) & 0xFF);
409cad9ecf6SJagpal Singh Gill             strValue += static_cast<char>(value & 0xFF);
410cad9ecf6SJagpal Singh Gill         }
411cad9ecf6SJagpal Singh Gill 
412cad9ecf6SJagpal Singh Gill         fillInventorySourceProperties(properties, reg.name, strValue);
413cad9ecf6SJagpal Singh Gill     }
414cad9ecf6SJagpal Singh Gill 
415cad9ecf6SJagpal Singh Gill     auto pathSuffix =
416cad9ecf6SJagpal Singh Gill         config.name + " " + std::to_string(address) + " " + portName;
417cad9ecf6SJagpal Singh Gill 
418cad9ecf6SJagpal Singh Gill     properties.name = pathSuffix;
419cad9ecf6SJagpal Singh Gill     properties.address = address;
420cad9ecf6SJagpal Singh Gill     properties.link_tty = portName;
421cad9ecf6SJagpal Singh Gill 
422cad9ecf6SJagpal Singh Gill     std::replace(pathSuffix.begin(), pathSuffix.end(), ' ', '_');
423cad9ecf6SJagpal Singh Gill 
424cad9ecf6SJagpal Singh Gill     auto objectPath =
425cad9ecf6SJagpal Singh Gill         std::string(InventorySourceIntf::namespace_path) + "/" + pathSuffix;
426cad9ecf6SJagpal Singh Gill     auto sourceId = std::to_string(address) + "_" + portName;
427cad9ecf6SJagpal Singh Gill 
428cad9ecf6SJagpal Singh Gill     inventorySources[sourceId] = std::make_unique<InventorySourceIntf>(
429cad9ecf6SJagpal Singh Gill         ctx, objectPath.c_str(), properties);
430cad9ecf6SJagpal Singh Gill     inventorySources[sourceId]->emit_added();
431cad9ecf6SJagpal Singh Gill 
432cad9ecf6SJagpal Singh Gill     info("Added InventorySource at {PATH}", "PATH", objectPath);
433cad9ecf6SJagpal Singh Gill }
434cad9ecf6SJagpal Singh Gill 
435cad9ecf6SJagpal Singh Gill } // namespace phosphor::modbus::rtu::inventory
436