Andrew Jeffery | 275f7c3 | 2024-01-31 12:41:14 +1030 | [diff] [blame] | 1 | #pragma once |
| 2 | |
| 3 | #include "Utils.hpp" |
| 4 | |
| 5 | #include <boost/asio/steady_timer.hpp> |
| 6 | #include <sdbusplus/asio/connection.hpp> |
| 7 | #include <sdbusplus/bus/match.hpp> |
| 8 | #include <sdbusplus/message.hpp> |
| 9 | #include <sdbusplus/message/native_types.hpp> |
| 10 | |
| 11 | #include <cstdint> |
| 12 | #include <iostream> |
| 13 | |
| 14 | /** |
| 15 | * @file |
| 16 | * @brief Abstract and concrete classes representing MCTP concepts and |
| 17 | * behaviours. |
| 18 | */ |
| 19 | |
| 20 | /** |
| 21 | * @brief An exception type that may be thrown by implementations of the MCTP |
| 22 | * abstract classes. |
| 23 | * |
| 24 | * This exception should be the basis for all exceptions thrown out of the MCTP |
| 25 | * APIs, and should capture any other exceptions that occur. |
| 26 | */ |
| 27 | class MCTPException : public std::exception |
| 28 | { |
| 29 | public: |
| 30 | MCTPException() = delete; |
| 31 | explicit MCTPException(const char* desc) : desc(desc) {} |
| 32 | const char* what() const noexcept override |
| 33 | { |
| 34 | return desc; |
| 35 | } |
| 36 | |
| 37 | private: |
| 38 | const char* desc; |
| 39 | }; |
| 40 | |
| 41 | /** |
| 42 | * @brief An enum of the MCTP transports described in DSP0239 v1.10.0 Section 7. |
| 43 | * |
| 44 | * https://www.dmtf.org/sites/default/files/standards/documents/DSP0239_1.10.0.pdf |
| 45 | */ |
| 46 | enum class MCTPTransport |
| 47 | { |
| 48 | Reserved = 0x00, |
| 49 | SMBus = 0x01, |
| 50 | }; |
| 51 | |
| 52 | /** |
| 53 | * @brief Captures properties of MCTP interfaces. |
| 54 | * |
Thu Nguyen | 19d1fda | 2024-12-05 08:43:06 +0000 | [diff] [blame] | 55 | * https://github.com/CodeConstruct/mctp/blob/v2.0/src/mctp.c#L668-L699 |
Andrew Jeffery | 275f7c3 | 2024-01-31 12:41:14 +1030 | [diff] [blame] | 56 | */ |
| 57 | struct MCTPInterface |
| 58 | { |
| 59 | std::string name; |
| 60 | MCTPTransport transport; |
| 61 | |
| 62 | auto operator<=>(const MCTPInterface& r) const = default; |
| 63 | }; |
| 64 | |
| 65 | class MCTPDevice; |
| 66 | |
| 67 | /** |
| 68 | * @brief Captures the behaviour of an endpoint at the MCTP layer |
| 69 | * |
| 70 | * The lifetime of an instance of MctpEndpoint is proportional to the lifetime |
| 71 | * of the endpoint configuration. If an endpoint is deconfigured such that its |
| 72 | * device has no assigned EID, then any related MctpEndpoint instance must be |
| 73 | * destructed as a consequence. |
| 74 | */ |
| 75 | class MCTPEndpoint |
| 76 | { |
| 77 | public: |
| 78 | using Event = std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)>; |
| 79 | using Result = std::function<void(const std::error_code& ec)>; |
| 80 | |
| 81 | virtual ~MCTPEndpoint() = default; |
| 82 | |
| 83 | /** |
| 84 | * @return The Linux network ID of the network in which the endpoint |
| 85 | participates |
| 86 | */ |
| 87 | virtual int network() const = 0; |
| 88 | |
| 89 | /** |
| 90 | * @return The MCTP endpoint ID of the endpoint in its network |
| 91 | */ |
| 92 | virtual uint8_t eid() const = 0; |
| 93 | |
| 94 | /** |
| 95 | * @brief Subscribe to events produced by an endpoint object across its |
| 96 | * lifecycle |
| 97 | * |
| 98 | * @param degraded The callback to execute when the MCTP layer indicates the |
| 99 | * endpoint is unresponsive |
| 100 | * |
| 101 | * @param available The callback to execute when the MCTP layer indicates |
| 102 | * that communication with the degraded endpoint has been |
| 103 | * recovered |
| 104 | * |
| 105 | * @param removed The callback to execute when the MCTP layer indicates the |
| 106 | * endpoint has been removed. |
| 107 | */ |
| 108 | virtual void subscribe(Event&& degraded, Event&& available, |
| 109 | Event&& removed) = 0; |
| 110 | |
| 111 | /** |
| 112 | * @brief Remove the endpoint from its associated network |
| 113 | */ |
| 114 | virtual void remove() = 0; |
| 115 | |
| 116 | /** |
| 117 | * @return A formatted string representing the endpoint in terms of its |
| 118 | * address properties |
| 119 | */ |
| 120 | virtual std::string describe() const = 0; |
| 121 | |
| 122 | /** |
| 123 | * @return A shared pointer to the device instance associated with the |
| 124 | * endpoint. |
| 125 | */ |
| 126 | virtual std::shared_ptr<MCTPDevice> device() const = 0; |
| 127 | }; |
| 128 | |
| 129 | /** |
| 130 | * @brief Represents an MCTP-capable device on a bus. |
| 131 | * |
| 132 | * It is often known that an MCTP-capable device exists on a bus prior to the |
| 133 | * MCTP stack configuring the device for communication. MctpDevice exposes the |
| 134 | * ability to set-up the endpoint device for communication. |
| 135 | * |
| 136 | * The lifetime of an MctpDevice instance is proportional to the existence of an |
| 137 | * MCTP-capable device in the system. If a device represented by an MctpDevice |
| 138 | * instance is removed from the system then any related MctpDevice instance must |
| 139 | * be destructed a consequence. |
| 140 | * |
| 141 | * Successful set-up of the device as an endpoint yields an MctpEndpoint |
| 142 | * instance. The lifetime of the MctpEndpoint instance produced must not exceed |
| 143 | * the lifetime of its parent MctpDevice. |
| 144 | */ |
| 145 | class MCTPDevice |
| 146 | { |
| 147 | public: |
| 148 | virtual ~MCTPDevice() = default; |
| 149 | |
| 150 | /** |
| 151 | * @brief Configure the device for MCTP communication |
| 152 | * |
| 153 | * @param added The callback to invoke once the setup process has |
| 154 | * completed. The provided error code @p ec must be |
| 155 | * checked as the request may not have succeeded. If |
| 156 | * the request was successful then @p ep contains a |
| 157 | * valid MctpEndpoint instance. |
| 158 | */ |
| 159 | virtual void |
| 160 | setup(std::function<void(const std::error_code& ec, |
| 161 | const std::shared_ptr<MCTPEndpoint>& ep)>&& |
| 162 | added) = 0; |
| 163 | |
| 164 | /** |
| 165 | * @brief Remove the device and any associated endpoint from the MCTP stack. |
| 166 | */ |
| 167 | virtual void remove() = 0; |
| 168 | |
| 169 | /** |
| 170 | * @return A formatted string representing the device in terms of its |
| 171 | * address properties. |
| 172 | */ |
| 173 | virtual std::string describe() const = 0; |
| 174 | }; |
| 175 | |
| 176 | class MCTPDDevice; |
| 177 | |
| 178 | /** |
| 179 | * @brief An implementation of MctpEndpoint in terms of the D-Bus interfaces |
| 180 | * exposed by @c mctpd. |
| 181 | * |
| 182 | * The lifetime of an MctpdEndpoint is proportional to the lifetime of the |
| 183 | * endpoint object exposed by @c mctpd. The lifecycle of @c mctpd endpoint |
| 184 | * objects is discussed here: |
| 185 | * |
| 186 | * https://github.com/CodeConstruct/mctp/pull/23/files#diff-00234f5f2543b8b9b8a419597e55121fe1cc57cf1c7e4ff9472bed83096bd28e |
| 187 | */ |
| 188 | class MCTPDEndpoint : |
| 189 | public MCTPEndpoint, |
| 190 | public std::enable_shared_from_this<MCTPDEndpoint> |
| 191 | { |
| 192 | public: |
| 193 | static std::string path(const std::shared_ptr<MCTPEndpoint>& ep); |
| 194 | |
| 195 | MCTPDEndpoint() = delete; |
| 196 | MCTPDEndpoint( |
| 197 | const std::shared_ptr<MCTPDDevice>& dev, |
| 198 | const std::shared_ptr<sdbusplus::asio::connection>& connection, |
| 199 | sdbusplus::message::object_path objpath, int network, uint8_t eid) : |
| 200 | dev(dev), connection(connection), objpath(std::move(objpath)), |
| 201 | mctp{network, eid} |
| 202 | {} |
| 203 | MCTPDEndpoint& McptdEndpoint(const MCTPDEndpoint& other) = delete; |
| 204 | MCTPDEndpoint(MCTPDEndpoint&& other) noexcept = default; |
| 205 | ~MCTPDEndpoint() override = default; |
| 206 | |
| 207 | int network() const override; |
| 208 | uint8_t eid() const override; |
| 209 | void subscribe(Event&& degraded, Event&& available, |
| 210 | Event&& removed) override; |
| 211 | void remove() override; |
| 212 | |
| 213 | std::string describe() const override; |
| 214 | |
| 215 | std::shared_ptr<MCTPDevice> device() const override; |
| 216 | |
| 217 | /** |
| 218 | * @brief Indicate the endpoint has been removed |
| 219 | * |
| 220 | * Called from the implementation of MctpdDevice for resource cleanup |
| 221 | * prior to destruction. Resource cleanup is delegated by invoking the |
| 222 | * notifyRemoved() callback. As the actions may be abitrary we avoid |
| 223 | * invoking notifyRemoved() in the destructor. |
| 224 | */ |
| 225 | void removed(); |
| 226 | |
| 227 | private: |
| 228 | std::shared_ptr<MCTPDDevice> dev; |
| 229 | std::shared_ptr<sdbusplus::asio::connection> connection; |
| 230 | sdbusplus::message::object_path objpath; |
| 231 | struct |
| 232 | { |
| 233 | int network; |
| 234 | uint8_t eid; |
| 235 | } mctp; |
| 236 | MCTPEndpoint::Event notifyAvailable; |
| 237 | MCTPEndpoint::Event notifyDegraded; |
| 238 | MCTPEndpoint::Event notifyRemoved; |
| 239 | std::optional<sdbusplus::bus::match_t> connectivityMatch; |
| 240 | |
| 241 | void onMctpEndpointChange(sdbusplus::message_t& msg); |
| 242 | void updateEndpointConnectivity(const std::string& connectivity); |
| 243 | }; |
| 244 | |
| 245 | /** |
| 246 | * @brief An implementation of MctpDevice in terms of D-Bus interfaces exposed |
| 247 | * by @c mctpd. |
| 248 | * |
| 249 | * The construction or destruction of an MctpdDevice is not required to be |
| 250 | * correlated with signals from @c mctpd. For instance, EntityManager may expose |
| 251 | * the existance of an MCTP-capable device through its usual configuration |
| 252 | * mechanisms. |
| 253 | */ |
| 254 | class MCTPDDevice : |
| 255 | public MCTPDevice, |
| 256 | public std::enable_shared_from_this<MCTPDDevice> |
| 257 | { |
| 258 | public: |
| 259 | MCTPDDevice() = delete; |
| 260 | MCTPDDevice(const std::shared_ptr<sdbusplus::asio::connection>& connection, |
| 261 | const std::string& interface, |
| 262 | const std::vector<uint8_t>& physaddr); |
| 263 | MCTPDDevice(const MCTPDDevice& other) = delete; |
| 264 | MCTPDDevice(MCTPDDevice&& other) = delete; |
| 265 | ~MCTPDDevice() override = default; |
| 266 | |
| 267 | void setup(std::function<void(const std::error_code& ec, |
| 268 | const std::shared_ptr<MCTPEndpoint>& ep)>&& |
| 269 | added) override; |
| 270 | void remove() override; |
| 271 | std::string describe() const override; |
| 272 | |
| 273 | private: |
| 274 | static void onEndpointInterfacesRemoved( |
| 275 | const std::weak_ptr<MCTPDDevice>& weak, const std::string& objpath, |
| 276 | sdbusplus::message_t& msg); |
| 277 | |
| 278 | std::shared_ptr<sdbusplus::asio::connection> connection; |
| 279 | const std::string interface; |
| 280 | const std::vector<uint8_t> physaddr; |
| 281 | std::shared_ptr<MCTPDEndpoint> endpoint; |
| 282 | std::unique_ptr<sdbusplus::bus::match_t> removeMatch; |
| 283 | |
| 284 | /** |
| 285 | * @brief Actions to perform once endpoint setup has succeeded |
| 286 | * |
| 287 | * Now that the endpoint exists two tasks remain: |
| 288 | * |
| 289 | * 1. Setup the match capturing removal of the endpoint object by mctpd |
| 290 | * 2. Invoke the callback to notify the requester that setup has completed, |
| 291 | * providing the MctpEndpoint instance associated with the MctpDevice. |
| 292 | */ |
| 293 | void finaliseEndpoint( |
| 294 | const std::string& objpath, uint8_t eid, int network, |
| 295 | std::function<void(const std::error_code& ec, |
| 296 | const std::shared_ptr<MCTPEndpoint>& ep)>& added); |
| 297 | void endpointRemoved(); |
| 298 | }; |
| 299 | |
| 300 | class I2CMCTPDDevice : public MCTPDDevice |
| 301 | { |
| 302 | public: |
| 303 | static std::optional<SensorBaseConfigMap> match(const SensorData& config); |
| 304 | static bool match(const std::set<std::string>& interfaces); |
| 305 | static std::shared_ptr<I2CMCTPDDevice> |
| 306 | from(const std::shared_ptr<sdbusplus::asio::connection>& connection, |
| 307 | const SensorBaseConfigMap& iface); |
| 308 | |
| 309 | I2CMCTPDDevice() = delete; |
| 310 | I2CMCTPDDevice( |
| 311 | const std::shared_ptr<sdbusplus::asio::connection>& connection, int bus, |
| 312 | uint8_t physaddr) : |
| 313 | MCTPDDevice(connection, interfaceFromBus(bus), {physaddr}) |
| 314 | {} |
| 315 | ~I2CMCTPDDevice() override = default; |
| 316 | |
| 317 | private: |
| 318 | static constexpr const char* configType = "MCTPI2CTarget"; |
| 319 | |
| 320 | static std::string interfaceFromBus(int bus); |
| 321 | }; |