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