1 #pragma once 2 3 #include "Utils.hpp" 4 5 #include <boost/container/flat_map.hpp> 6 7 #include <functional> 8 #include <optional> 9 #include <string_view> 10 11 struct I2CDeviceType 12 { 13 const char* name; 14 bool createsHWMon; 15 }; 16 17 struct I2CDeviceComparator 18 { 19 bool operator()(const std::string& a, const std::string& b) const noexcept 20 { 21 return strcasecmp(a.c_str(), b.c_str()) < 0; 22 } 23 }; 24 25 using I2CDeviceTypeMap = 26 boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>; 27 28 struct I2CDeviceParams 29 { 30 I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) : 31 type(&type), bus(bus), address(address){}; 32 33 const I2CDeviceType* type; 34 uint64_t bus; 35 uint64_t address; 36 37 bool devicePresent(void) const; 38 bool deviceStatic(void) const; 39 }; 40 41 std::optional<I2CDeviceParams> 42 getI2CDeviceParams(const I2CDeviceTypeMap& dtmap, 43 const SensorBaseConfigMap& cfg); 44 45 class I2CDevice 46 { 47 public: 48 explicit I2CDevice(I2CDeviceParams params); 49 ~I2CDevice(); 50 51 private: 52 I2CDeviceParams params; 53 54 int create(void) const; 55 int destroy(void) const; 56 }; 57 58 // HACK: this declaration "should" live in Utils.hpp, but that leads to a 59 // tangle of header-dependency hell because each header needs types declared 60 // in the other. 61 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> 62 setupPropertiesChangedMatches( 63 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap, 64 const std::function<void(sdbusplus::message_t&)>& handler); 65 66 // returns a {path: <I2CDevice, is_new>} map. is_new indicates if the I2CDevice 67 // is newly instantiated by this call (true) or was already there (false). 68 template <class T> 69 boost::container::flat_map<std::string, 70 std::pair<std::shared_ptr<I2CDevice>, bool>> 71 instantiateDevices( 72 const ManagedObjectType& sensorConfigs, 73 const boost::container::flat_map<std::string, std::shared_ptr<T>>& 74 sensors, 75 const I2CDeviceTypeMap& sensorTypes) 76 { 77 boost::container::flat_map<std::string, 78 std::pair<std::shared_ptr<I2CDevice>, bool>> 79 devices; 80 for (const auto& [path, sensor] : sensorConfigs) 81 { 82 for (const auto& [name, cfg] : sensor) 83 { 84 PowerState powerState = getPowerState(cfg); 85 if (!readingStateGood(powerState)) 86 { 87 continue; 88 } 89 90 auto findSensorName = cfg.find("Name"); 91 if (findSensorName == cfg.end()) 92 { 93 continue; 94 } 95 std::string sensorName = 96 std::get<std::string>(findSensorName->second); 97 98 auto findSensor = sensors.find(sensorName); 99 if (findSensor != sensors.end() && findSensor->second != nullptr && 100 findSensor->second->isActive()) 101 { 102 devices.emplace( 103 path.str, 104 std::make_pair(findSensor->second->getI2CDevice(), false)); 105 continue; 106 } 107 108 std::optional<I2CDeviceParams> params = 109 getI2CDeviceParams(sensorTypes, cfg); 110 if (params.has_value() && !params->deviceStatic()) 111 { 112 // There exist error cases in which a sensor device that we 113 // need is already instantiated, but needs to be destroyed and 114 // re-created in order to be useful (for example if we crash 115 // after instantiating a device and the sensor device's power 116 // is cut before we get restarted, leaving it "present" but 117 // not really usable). To be on the safe side, instantiate a 118 // temporary device that's immediately destroyed so as to 119 // ensure that we end up with a fresh instance of it. 120 if (params->devicePresent()) 121 { 122 std::cerr << "Clearing out previous instance for " 123 << path.str << "\n"; 124 I2CDevice tmp(*params); 125 } 126 127 try 128 { 129 devices.emplace( 130 path.str, 131 std::make_pair(std::make_shared<I2CDevice>(*params), 132 true)); 133 } 134 catch (std::runtime_error&) 135 { 136 std::cerr << "Failed to instantiate " << params->type->name 137 << " at address " << params->address << " on bus " 138 << params->bus << "\n"; 139 } 140 } 141 } 142 } 143 return devices; 144 } 145