1 #include "base_config.hpp"
2
3 #include "common/entity_manager_interface.hpp"
4
5 #include <phosphor-logging/lg2.hpp>
6 #include <xyz/openbmc_project/Inventory/Item/client.hpp>
7 #include <xyz/openbmc_project/State/Leak/Detector/aserver.hpp>
8
9 #include <flat_map>
10
11 namespace phosphor::modbus::rtu::device::config
12 {
13
14 PHOSPHOR_LOG2_USING;
15
16 using BasicVariantType =
17 std::variant<std::vector<std::string>, std::vector<uint8_t>, std::string,
18 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
19 uint16_t, uint8_t, bool>;
20 using InventoryBaseConfigMap = std::flat_map<std::string, BasicVariantType>;
21 using InventoryData = std::flat_map<std::string, InventoryBaseConfigMap>;
22 using ManagedObjectType =
23 std::flat_map<sdbusplus::message::object_path, InventoryData>;
24
25 static constexpr std::array<std::pair<std::string_view, Parity>, 3>
26 validParities = {
27 {{"Odd", Parity::odd}, {"Even", Parity::even}, {"None", Parity::none}}};
28
29 template <typename T>
getValue(const InventoryBaseConfigMap & configMap,const std::string & key,const std::string & contextName)30 auto getValue(const InventoryBaseConfigMap& configMap, const std::string& key,
31 const std::string& contextName) -> T
32 {
33 auto iter = configMap.find(key);
34 if (iter == configMap.end())
35 {
36 throw std::runtime_error(
37 "Missing property " + key + " for " + contextName);
38 }
39
40 try
41 {
42 return std::get<T>(iter->second);
43 }
44 catch (const std::bad_variant_access& ex)
45 {
46 throw std::runtime_error(
47 "Incorrect type for property " + key + " in " + contextName);
48 }
49 }
50
getDataParity(Config & config,const InventoryBaseConfigMap & configMap)51 static inline auto getDataParity(Config& config,
52 const InventoryBaseConfigMap& configMap)
53 -> void
54 {
55 auto receivedParity =
56 getValue<std::string>(configMap, "DataParity", config.name);
57
58 for (const auto& [parityStr, parity] : validParities)
59 {
60 if (parityStr == receivedParity)
61 {
62 config.parity = parity;
63 break;
64 }
65 }
66
67 if (config.parity == Parity::unknown)
68 {
69 throw std::runtime_error(
70 "Invalid parity " + receivedParity + " for " + config.name);
71 }
72 }
73
processDeviceInterface(Config & config,const InventoryBaseConfigMap & configMap)74 static auto processDeviceInterface(Config& config,
75 const InventoryBaseConfigMap& configMap)
76 -> void
77 {
78 debug("Processing device config");
79
80 config.name = getValue<std::string>(configMap, "Name", config.name);
81
82 std::replace(config.name.begin(), config.name.end(), ' ', '_');
83
84 config.address = getValue<uint64_t>(configMap, "Address", config.name);
85
86 getDataParity(config, configMap);
87
88 config.baudRate = getValue<uint64_t>(configMap, "BaudRate", config.name);
89
90 config.portName =
91 getValue<std::string>(configMap, "SerialPort", config.name);
92
93 getValue<std::string>(configMap, "Type", config.name);
94 }
95
96 static const auto sensorTypes = std::unordered_map<
97 std::string_view, std::pair<std::string_view, SensorValueIntf::Unit>>{
98 {"FanTach",
99 {SensorValueIntf::namespace_path::fan_tach, SensorValueIntf::Unit::RPMS}},
100 {"LiquidFlow",
101 {SensorValueIntf::namespace_path::liquidflow, SensorValueIntf::Unit::LPM}},
102 {"Power",
103 {SensorValueIntf::namespace_path::power, SensorValueIntf::Unit::Watts}},
104 {"Pressure",
105 {SensorValueIntf::namespace_path::pressure,
106 SensorValueIntf::Unit::Pascals}},
107 {"Temperature",
108 {SensorValueIntf::namespace_path::temperature,
109 SensorValueIntf::Unit::DegreesC}},
110 };
111
112 static const auto formatTypes =
113 std::unordered_map<std::string_view, SensorFormat>{
114 {"Integer", SensorFormat::integer},
115 {"Float", SensorFormat::floatingPoint}};
116
processRegisterType(SensorRegister & sensorRegister,const InventoryBaseConfigMap & configMap)117 static auto processRegisterType(SensorRegister& sensorRegister,
118 const InventoryBaseConfigMap& configMap) -> void
119 {
120 auto registerType =
121 getValue<std::string>(configMap, "RegisterType", sensorRegister.name);
122
123 auto type = sensorTypes.find(registerType);
124 if (type == sensorTypes.end())
125 {
126 throw std::runtime_error("Invalid RegisterType " + registerType +
127 " for " + sensorRegister.name);
128 }
129 sensorRegister.pathSuffix = type->second.first;
130 sensorRegister.unit = type->second.second;
131 }
132
processRegisterFormat(SensorRegister & sensorRegister,const InventoryBaseConfigMap & configMap)133 static auto processRegisterFormat(SensorRegister& sensorRegister,
134 const InventoryBaseConfigMap& configMap)
135 -> void
136 {
137 auto format =
138 getValue<std::string>(configMap, "Format", sensorRegister.name);
139
140 auto formatIter = formatTypes.find(format);
141 if (formatIter == formatTypes.end())
142 {
143 throw std::runtime_error(
144 "Invalid Format " + format + " for " + sensorRegister.name);
145 }
146 sensorRegister.format = formatIter->second;
147 }
148
processSensorRegistersInterface(Config & config,const InventoryBaseConfigMap & configMap)149 static auto processSensorRegistersInterface(
150 Config& config, const InventoryBaseConfigMap& configMap) -> void
151 {
152 SensorRegister sensorRegister = {};
153
154 sensorRegister.name = getValue<std::string>(configMap, "Name", config.name);
155
156 processRegisterType(sensorRegister, configMap);
157
158 sensorRegister.offset =
159 getValue<uint64_t>(configMap, "Address", config.name);
160
161 sensorRegister.size = getValue<uint64_t>(configMap, "Size", config.name);
162
163 sensorRegister.precision =
164 getValue<uint64_t>(configMap, "Precision", config.name);
165
166 sensorRegister.shift = getValue<double>(configMap, "Shift", config.name);
167
168 sensorRegister.scale = getValue<double>(configMap, "Scale", config.name);
169
170 sensorRegister.isSigned = getValue<bool>(configMap, "Signed", config.name);
171
172 processRegisterFormat(sensorRegister, configMap);
173
174 config.sensorRegisters.emplace_back(sensorRegister);
175 }
176
177 static const auto statusBitTypes =
178 std::unordered_map<std::string_view, StatusType>{
179 {"ControllerFailure", StatusType::controllerFailure},
180 {"FanFailure", StatusType::fanFailure},
181 {"FilterFailure", StatusType::filterFailure},
182 {"PowerFault", StatusType::powerFault},
183 {"PumpFailure", StatusType::pumpFailure},
184 {"LeakDetectedCritical", StatusType::leakDetectedCritical},
185 {"LeakDetectedWarning", StatusType::leakDetectedWarning},
186 {"SensorFailure", StatusType::sensorFailure},
187 {"SensorReadingCritical", StatusType::sensorReadingCritical},
188 {"SensorReadingWarning", StatusType::sensorReadingWarning}};
189
processStatusBitsInterface(Config & config,const InventoryBaseConfigMap & configMap)190 static auto processStatusBitsInterface(Config& config,
191 const InventoryBaseConfigMap& configMap)
192 -> void
193 {
194 debug("Processing StatusBits for {NAME}", "NAME", config.name);
195
196 StatusBit statusBit = {};
197
198 statusBit.name = getValue<std::string>(configMap, "Name", config.name);
199
200 auto type = getValue<std::string>(configMap, "StatusType", config.name);
201 auto typeIter = statusBitTypes.find(type);
202 if (typeIter == statusBitTypes.end())
203 {
204 throw std::runtime_error(
205 "Invalid StatusType " + type + " for " + statusBit.name);
206 }
207 statusBit.type = typeIter->second;
208
209 statusBit.bitPosition =
210 getValue<uint64_t>(configMap, "BitPosition", config.name);
211
212 statusBit.value = getValue<bool>(configMap, "Value", config.name);
213
214 auto address = getValue<uint64_t>(configMap, "Address", config.name);
215
216 config.statusRegisters[address].emplace_back(statusBit);
217 }
218
219 static const auto firmwareRegisterTypes =
220 std::unordered_map<std::string_view, FirmwareRegisterType>{
221 {"Version", FirmwareRegisterType::version},
222 {"Update", FirmwareRegisterType::update}};
223
processFirmwareRegistersInterface(Config & config,const InventoryBaseConfigMap & configMap)224 static auto processFirmwareRegistersInterface(
225 Config& config, const InventoryBaseConfigMap& configMap) -> void
226 {
227 debug("Processing FirmwareRegisters for {NAME}", "NAME", config.name);
228
229 FirmwareRegister firmwareRegister = {};
230
231 firmwareRegister.name =
232 getValue<std::string>(configMap, "Name", config.name);
233
234 firmwareRegister.offset =
235 getValue<uint64_t>(configMap, "Address", firmwareRegister.name);
236
237 firmwareRegister.size =
238 getValue<uint64_t>(configMap, "Size", firmwareRegister.name);
239
240 auto registerType =
241 getValue<std::string>(configMap, "RegisterType", firmwareRegister.name);
242 auto registerTypeIter = firmwareRegisterTypes.find(registerType);
243 if (registerTypeIter == firmwareRegisterTypes.end())
244 {
245 throw std::runtime_error("Invalid RegisterType " + registerType +
246 " for " + firmwareRegister.name);
247 }
248 firmwareRegister.type = registerTypeIter->second;
249
250 config.firmwareRegisters.emplace_back(firmwareRegister);
251 }
252
printConfig(const Config & config)253 static auto printConfig(const Config& config) -> void
254 {
255 info("Device Config for {NAME}: {ADDRESS} {PORT} {INV_PATH}", "NAME",
256 config.name, "ADDRESS", config.address, "PORT", config.portName,
257 "INV_PATH", config.inventoryPath);
258
259 for (const auto& sensorRegister : config.sensorRegisters)
260 {
261 info(
262 "Sensor Register {NAME} {ADDRESS} {SIZE} {PRECISION} {SCALE} {SIGNED} {FORMAT} {UNIT} {PATH_SUFFIX}",
263 "NAME", sensorRegister.name, "ADDRESS", sensorRegister.offset,
264 "SIZE", sensorRegister.size, "PRECISION", sensorRegister.precision,
265 "SCALE", sensorRegister.scale, "SIGNED", sensorRegister.isSigned,
266 "FORMAT", sensorRegister.format, "UNIT", sensorRegister.unit,
267 "PATH_SUFFIX", sensorRegister.pathSuffix);
268 }
269
270 for (const auto& [address, statusBits] : config.statusRegisters)
271 {
272 for (const auto& statusBit : statusBits)
273 {
274 info("Status Bit {NAME} {ADDRESS} {BIT_POSITION} {VALUE} {TYPE}",
275 "NAME", statusBit.name, "ADDRESS", address, "BIT_POSITION",
276 statusBit.bitPosition, "VALUE", statusBit.value, "TYPE",
277 statusBit.type);
278 }
279 }
280
281 for (const auto& firmwareRegister : config.firmwareRegisters)
282 {
283 info("Firmware Register {NAME} {ADDRESS} {SIZE} {TYPE}", "NAME",
284 firmwareRegister.name, "ADDRESS", firmwareRegister.offset, "SIZE",
285 firmwareRegister.size, "TYPE", firmwareRegister.type);
286 }
287 }
288
getConfigSubTree(Config & config,const std::string & interfaceName,const InventoryData & deviceConfig)289 static auto getConfigSubTree(Config& config, const std::string& interfaceName,
290 const InventoryData& deviceConfig) -> void
291 {
292 std::string firmwareRegistersInterface =
293 interfaceName + ".FirmwareRegisters";
294 std::string sensorRegistersInterface = interfaceName + ".SensorRegisters";
295 std::string statusBitsInterface = interfaceName + ".StatusBits";
296
297 for (const auto& [curInterface, interfaceConfig] : deviceConfig)
298 {
299 if (curInterface == interfaceName)
300 {
301 processDeviceInterface(config, interfaceConfig);
302 }
303 else if (curInterface.starts_with(sensorRegistersInterface))
304 {
305 processSensorRegistersInterface(config, interfaceConfig);
306 }
307 else if (curInterface.starts_with(statusBitsInterface))
308 {
309 processStatusBitsInterface(config, interfaceConfig);
310 }
311 else if (curInterface.starts_with(firmwareRegistersInterface))
312 {
313 processFirmwareRegistersInterface(config, interfaceConfig);
314 }
315 }
316 }
317
updateBaseConfig(sdbusplus::async::context & ctx,const sdbusplus::message::object_path & objectPath,const std::string & interfaceName,Config & config)318 auto updateBaseConfig(sdbusplus::async::context& ctx,
319 const sdbusplus::message::object_path& objectPath,
320 const std::string& interfaceName, Config& config)
321 -> sdbusplus::async::task<bool>
322 {
323 config.inventoryPath = objectPath.parent_path();
324
325 using InventoryIntf =
326 sdbusplus::client::xyz::openbmc_project::inventory::Item<>;
327
328 constexpr auto entityManager =
329 sdbusplus::async::proxy()
330 .service(entity_manager::EntityManagerInterface::serviceName)
331 .path(InventoryIntf::namespace_path)
332 .interface("org.freedesktop.DBus.ObjectManager");
333
334 for (const auto& [path, deviceConfig] :
335 co_await entityManager.call<ManagedObjectType>(ctx,
336 "GetManagedObjects"))
337 {
338 if (path.str != objectPath.str)
339 {
340 debug("Skipping device {PATH}", "PATH", path.str);
341 continue;
342 }
343 debug("Processing device {PATH}", "PATH", path.str);
344
345 try
346 {
347 getConfigSubTree(config, interfaceName, deviceConfig);
348 }
349 catch (std::exception& e)
350 {
351 error("Failed to process device {PATH} with {ERROR}", "PATH",
352 path.str, "ERROR", e);
353 co_return false;
354 }
355 }
356
357 printConfig(config);
358
359 co_return true;
360 }
361
362 } // namespace phosphor::modbus::rtu::device::config
363