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