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; MCTPException(const char * desc)31 explicit MCTPException(const char* desc) : desc(desc) {} what() const32 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 * 55 * https://github.com/CodeConstruct/mctp/blob/v1.1/src/mctp.c#L672-L703 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; 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)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; I2CMCTPDDevice(const std::shared_ptr<sdbusplus::asio::connection> & connection,int bus,uint8_t physaddr)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 }; 322