Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 1 | #pragma once |
Andrew Jeffery | e73bd0a | 2023-01-25 10:39:57 +1030 | [diff] [blame] | 2 | |
| 3 | #include "Utils.hpp" |
| 4 | |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 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 | |
Matt Simmering | 786efb8 | 2023-01-18 14:09:21 -0800 | [diff] [blame] | 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 | |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 25 | using I2CDeviceTypeMap = |
Matt Simmering | 786efb8 | 2023-01-18 14:09:21 -0800 | [diff] [blame] | 26 | boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>; |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 27 | |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 28 | struct I2CDeviceParams |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 29 | { |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 30 | I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) : |
Patrick Williams | 2aaf717 | 2024-08-16 15:20:40 -0400 | [diff] [blame^] | 31 | type(&type), bus(bus), address(address) {}; |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 32 | |
| 33 | const I2CDeviceType* type; |
| 34 | uint64_t bus; |
| 35 | uint64_t address; |
| 36 | |
Ed Tanous | 201a101 | 2024-04-03 18:07:28 -0700 | [diff] [blame] | 37 | bool devicePresent() const; |
| 38 | bool deviceStatic() const; |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 39 | }; |
| 40 | |
Patrick Williams | 2aaf717 | 2024-08-16 15:20:40 -0400 | [diff] [blame^] | 41 | std::optional<I2CDeviceParams> getI2CDeviceParams( |
| 42 | const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg); |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 43 | |
| 44 | class I2CDevice |
| 45 | { |
| 46 | public: |
| 47 | explicit I2CDevice(I2CDeviceParams params); |
| 48 | ~I2CDevice(); |
| 49 | |
| 50 | private: |
| 51 | I2CDeviceParams params; |
| 52 | |
Ed Tanous | 201a101 | 2024-04-03 18:07:28 -0700 | [diff] [blame] | 53 | int create() const; |
| 54 | int destroy() const; |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 55 | }; |
| 56 | |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 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); |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 64 | |
Matt Simmering | cafd72f | 2022-12-16 15:35:12 -0800 | [diff] [blame] | 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 | |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 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 | } |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 104 | |
Matt Simmering | cafd72f | 2022-12-16 15:35:12 -0800 | [diff] [blame] | 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()) |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 125 | { |
| 126 | devices.emplace( |
| 127 | path.str, |
Matt Simmering | cafd72f | 2022-12-16 15:35:12 -0800 | [diff] [blame] | 128 | std::make_pair(findSensor->getI2CDevice(), false)); |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 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 | } |