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