blob: ffba380995e870ac0e1656af89ba815b62f2d27b [file] [log] [blame]
Andrew Jeffery275f7c32024-01-31 12:41:14 +10301#pragma once
2
3#include "Utils.hpp"
4
Andrew Jeffery275f7c32024-01-31 12:41:14 +10305#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>
Ed Tanous18b61862025-01-30 10:56:28 -080011#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>
Andrew Jeffery275f7c32024-01-31 12:41:14 +103020
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 */
34class MCTPException : public std::exception
35{
36 public:
37 MCTPException() = delete;
38 explicit MCTPException(const char* desc) : desc(desc) {}
39 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 */
53enum class MCTPTransport
54{
55 Reserved = 0x00,
56 SMBus = 0x01,
57};
58
59/**
60 * @brief Captures properties of MCTP interfaces.
61 *
Thu Nguyen19d1fda2024-12-05 08:43:06 +000062 * https://github.com/CodeConstruct/mctp/blob/v2.0/src/mctp.c#L668-L699
Andrew Jeffery275f7c32024-01-31 12:41:14 +103063 */
64struct MCTPInterface
65{
66 std::string name;
67 MCTPTransport transport;
68
69 auto operator<=>(const MCTPInterface& r) const = default;
70};
71
72class 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 */
82class 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 */
152class 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 */
Patrick Williams556e04b2025-02-01 08:22:22 -0500166 virtual void setup(
167 std::function<void(const std::error_code& ec,
168 const std::shared_ptr<MCTPEndpoint>& ep)>&&
169 added) = 0;
Andrew Jeffery275f7c32024-01-31 12:41:14 +1030170
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
183class 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 */
195class 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;
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 */
261class 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
307class 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);
Patrick Williams556e04b2025-02-01 08:22:22 -0500312 static std::shared_ptr<I2CMCTPDDevice> from(
313 const std::shared_ptr<sdbusplus::asio::connection>& connection,
314 const SensorBaseConfigMap& iface);
Andrew Jeffery275f7c32024-01-31 12:41:14 +1030315
316 I2CMCTPDDevice() = delete;
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};