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() const; 38 bool deviceStatic() 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() const; 55 int destroy() 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 // Helper find function because some sensors use underscores in their names 67 // while others use spaces. Ignore this discrepancy by changing all spaces to 68 // underscores. 69 inline size_t sensorNameFind(const std::string& fullName, 70 const std::string& partialName) 71 { 72 return boost::replace_all_copy(fullName, " ", "_") 73 .find(boost::replace_all_copy(partialName, " ", "_")); 74 } 75 76 // returns a {path: <I2CDevice, is_new>} map. is_new indicates if the I2CDevice 77 // is newly instantiated by this call (true) or was already there (false). 78 template <class T> 79 boost::container::flat_map<std::string, 80 std::pair<std::shared_ptr<I2CDevice>, bool>> 81 instantiateDevices( 82 const ManagedObjectType& sensorConfigs, 83 const boost::container::flat_map<std::string, std::shared_ptr<T>>& 84 sensors, 85 const I2CDeviceTypeMap& sensorTypes) 86 { 87 boost::container::flat_map<std::string, 88 std::pair<std::shared_ptr<I2CDevice>, bool>> 89 devices; 90 for (const auto& [path, sensor] : sensorConfigs) 91 { 92 for (const auto& [name, cfg] : sensor) 93 { 94 PowerState powerState = getPowerState(cfg); 95 if (!readingStateGood(powerState)) 96 { 97 continue; 98 } 99 100 auto findSensorName = cfg.find("Name"); 101 if (findSensorName == cfg.end()) 102 { 103 continue; 104 } 105 106 const auto* sensorName = 107 std::get_if<std::string>(&findSensorName->second); 108 if (sensorName == nullptr) 109 { 110 std::cerr << "Unable to find sensor name " << name 111 << " on path " << path.str << "\n"; 112 continue; 113 } 114 115 std::shared_ptr<T> findSensor(nullptr); 116 for (const auto& sensor : sensors) 117 { 118 if (sensorNameFind(sensor.first, *sensorName) != 119 std::string::npos) 120 { 121 findSensor = sensor.second; 122 break; 123 } 124 } 125 if (findSensor != nullptr && findSensor->isActive()) 126 { 127 devices.emplace( 128 path.str, 129 std::make_pair(findSensor->getI2CDevice(), false)); 130 continue; 131 } 132 133 std::optional<I2CDeviceParams> params = 134 getI2CDeviceParams(sensorTypes, cfg); 135 if (params.has_value() && !params->deviceStatic()) 136 { 137 // There exist error cases in which a sensor device that we 138 // need is already instantiated, but needs to be destroyed and 139 // re-created in order to be useful (for example if we crash 140 // after instantiating a device and the sensor device's power 141 // is cut before we get restarted, leaving it "present" but 142 // not really usable). To be on the safe side, instantiate a 143 // temporary device that's immediately destroyed so as to 144 // ensure that we end up with a fresh instance of it. 145 if (params->devicePresent()) 146 { 147 std::cerr << "Clearing out previous instance for " 148 << path.str << "\n"; 149 I2CDevice tmp(*params); 150 } 151 152 try 153 { 154 devices.emplace( 155 path.str, 156 std::make_pair(std::make_shared<I2CDevice>(*params), 157 true)); 158 } 159 catch (std::runtime_error&) 160 { 161 std::cerr << "Failed to instantiate " << params->type->name 162 << " at address " << params->address << " on bus " 163 << params->bus << "\n"; 164 } 165 } 166 } 167 } 168 return devices; 169 } 170