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 | |
Ed Tanous | 18b6186 | 2025-01-30 10:56:28 -0800 | [diff] [blame] | 5 | #include <strings.h> |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 6 | |
Ed Tanous | 18b6186 | 2025-01-30 10:56:28 -0800 | [diff] [blame] | 7 | #include <boost/algorithm/string/replace.hpp> |
| 8 | #include <boost/container/flat_map.hpp> |
| 9 | #include <sdbusplus/asio/connection.hpp> |
| 10 | #include <sdbusplus/bus/match.hpp> |
| 11 | #include <sdbusplus/message.hpp> |
| 12 | |
| 13 | #include <cstddef> |
| 14 | #include <cstdint> |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 15 | #include <functional> |
Ed Tanous | 18b6186 | 2025-01-30 10:56:28 -0800 | [diff] [blame] | 16 | #include <iostream> |
| 17 | #include <memory> |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 18 | #include <optional> |
Ed Tanous | 18b6186 | 2025-01-30 10:56:28 -0800 | [diff] [blame] | 19 | #include <stdexcept> |
| 20 | #include <string> |
| 21 | #include <utility> |
| 22 | #include <variant> |
| 23 | #include <vector> |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 24 | |
| 25 | struct I2CDeviceType |
| 26 | { |
| 27 | const char* name; |
| 28 | bool createsHWMon; |
| 29 | }; |
| 30 | |
Matt Simmering | 786efb8 | 2023-01-18 14:09:21 -0800 | [diff] [blame] | 31 | struct I2CDeviceComparator |
| 32 | { |
| 33 | bool operator()(const std::string& a, const std::string& b) const noexcept |
| 34 | { |
| 35 | return strcasecmp(a.c_str(), b.c_str()) < 0; |
| 36 | } |
| 37 | }; |
| 38 | |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 39 | using I2CDeviceTypeMap = |
Matt Simmering | 786efb8 | 2023-01-18 14:09:21 -0800 | [diff] [blame] | 40 | boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>; |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 41 | |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 42 | struct I2CDeviceParams |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 43 | { |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 44 | I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) : |
Patrick Williams | 2aaf717 | 2024-08-16 15:20:40 -0400 | [diff] [blame] | 45 | type(&type), bus(bus), address(address) {}; |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 46 | |
| 47 | const I2CDeviceType* type; |
| 48 | uint64_t bus; |
| 49 | uint64_t address; |
| 50 | |
Ed Tanous | 201a101 | 2024-04-03 18:07:28 -0700 | [diff] [blame] | 51 | bool devicePresent() const; |
| 52 | bool deviceStatic() const; |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 53 | }; |
| 54 | |
Patrick Williams | 2aaf717 | 2024-08-16 15:20:40 -0400 | [diff] [blame] | 55 | std::optional<I2CDeviceParams> getI2CDeviceParams( |
| 56 | const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg); |
Zev Weiss | 3ee959a | 2022-09-21 17:16:28 -0700 | [diff] [blame] | 57 | |
| 58 | class I2CDevice |
| 59 | { |
| 60 | public: |
| 61 | explicit I2CDevice(I2CDeviceParams params); |
| 62 | ~I2CDevice(); |
| 63 | |
| 64 | private: |
| 65 | I2CDeviceParams params; |
| 66 | |
Ed Tanous | 201a101 | 2024-04-03 18:07:28 -0700 | [diff] [blame] | 67 | int create() const; |
| 68 | int destroy() const; |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 69 | }; |
| 70 | |
Zev Weiss | dabd48d | 2022-08-03 15:43:17 -0700 | [diff] [blame] | 71 | // HACK: this declaration "should" live in Utils.hpp, but that leads to a |
| 72 | // tangle of header-dependency hell because each header needs types declared |
| 73 | // in the other. |
| 74 | std::vector<std::unique_ptr<sdbusplus::bus::match_t>> |
| 75 | setupPropertiesChangedMatches( |
| 76 | sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap, |
| 77 | const std::function<void(sdbusplus::message_t&)>& handler); |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 78 | |
Matt Simmering | cafd72f | 2022-12-16 15:35:12 -0800 | [diff] [blame] | 79 | // Helper find function because some sensors use underscores in their names |
| 80 | // while others use spaces. Ignore this discrepancy by changing all spaces to |
| 81 | // underscores. |
| 82 | inline size_t sensorNameFind(const std::string& fullName, |
| 83 | const std::string& partialName) |
| 84 | { |
| 85 | return boost::replace_all_copy(fullName, " ", "_") |
| 86 | .find(boost::replace_all_copy(partialName, " ", "_")); |
| 87 | } |
| 88 | |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 89 | // returns a {path: <I2CDevice, is_new>} map. is_new indicates if the I2CDevice |
| 90 | // is newly instantiated by this call (true) or was already there (false). |
| 91 | template <class T> |
| 92 | boost::container::flat_map<std::string, |
| 93 | std::pair<std::shared_ptr<I2CDevice>, bool>> |
| 94 | instantiateDevices( |
| 95 | const ManagedObjectType& sensorConfigs, |
| 96 | const boost::container::flat_map<std::string, std::shared_ptr<T>>& |
| 97 | sensors, |
| 98 | const I2CDeviceTypeMap& sensorTypes) |
| 99 | { |
| 100 | boost::container::flat_map<std::string, |
| 101 | std::pair<std::shared_ptr<I2CDevice>, bool>> |
| 102 | devices; |
| 103 | for (const auto& [path, sensor] : sensorConfigs) |
| 104 | { |
| 105 | for (const auto& [name, cfg] : sensor) |
| 106 | { |
| 107 | PowerState powerState = getPowerState(cfg); |
| 108 | if (!readingStateGood(powerState)) |
| 109 | { |
| 110 | continue; |
| 111 | } |
| 112 | |
| 113 | auto findSensorName = cfg.find("Name"); |
| 114 | if (findSensorName == cfg.end()) |
| 115 | { |
| 116 | continue; |
| 117 | } |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 118 | |
Matt Simmering | cafd72f | 2022-12-16 15:35:12 -0800 | [diff] [blame] | 119 | const auto* sensorName = |
| 120 | std::get_if<std::string>(&findSensorName->second); |
| 121 | if (sensorName == nullptr) |
| 122 | { |
| 123 | std::cerr << "Unable to find sensor name " << name |
| 124 | << " on path " << path.str << "\n"; |
| 125 | continue; |
| 126 | } |
| 127 | |
| 128 | std::shared_ptr<T> findSensor(nullptr); |
| 129 | for (const auto& sensor : sensors) |
| 130 | { |
| 131 | if (sensorNameFind(sensor.first, *sensorName) != |
| 132 | std::string::npos) |
| 133 | { |
| 134 | findSensor = sensor.second; |
| 135 | break; |
| 136 | } |
| 137 | } |
| 138 | if (findSensor != nullptr && findSensor->isActive()) |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 139 | { |
| 140 | devices.emplace( |
| 141 | path.str, |
Matt Simmering | cafd72f | 2022-12-16 15:35:12 -0800 | [diff] [blame] | 142 | std::make_pair(findSensor->getI2CDevice(), false)); |
Matt Simmering | c564eda | 2023-05-12 13:04:43 -0700 | [diff] [blame] | 143 | continue; |
| 144 | } |
| 145 | |
| 146 | std::optional<I2CDeviceParams> params = |
| 147 | getI2CDeviceParams(sensorTypes, cfg); |
| 148 | if (params.has_value() && !params->deviceStatic()) |
| 149 | { |
| 150 | // There exist error cases in which a sensor device that we |
| 151 | // need is already instantiated, but needs to be destroyed and |
| 152 | // re-created in order to be useful (for example if we crash |
| 153 | // after instantiating a device and the sensor device's power |
| 154 | // is cut before we get restarted, leaving it "present" but |
| 155 | // not really usable). To be on the safe side, instantiate a |
| 156 | // temporary device that's immediately destroyed so as to |
| 157 | // ensure that we end up with a fresh instance of it. |
| 158 | if (params->devicePresent()) |
| 159 | { |
| 160 | std::cerr << "Clearing out previous instance for " |
| 161 | << path.str << "\n"; |
| 162 | I2CDevice tmp(*params); |
| 163 | } |
| 164 | |
| 165 | try |
| 166 | { |
| 167 | devices.emplace( |
| 168 | path.str, |
| 169 | std::make_pair(std::make_shared<I2CDevice>(*params), |
| 170 | true)); |
| 171 | } |
| 172 | catch (std::runtime_error&) |
| 173 | { |
| 174 | std::cerr << "Failed to instantiate " << params->type->name |
| 175 | << " at address " << params->address << " on bus " |
| 176 | << params->bus << "\n"; |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | return devices; |
| 182 | } |