blob: f1b219824b76eddc2cf0f5c1d7cb28fb3e88c96b [file] [log] [blame]
Andrew Jeffery275f7c32024-01-31 12:41:14 +10301#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 */
27class 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 */
46enum class MCTPTransport
47{
48 Reserved = 0x00,
49 SMBus = 0x01,
50};
51
52/**
53 * @brief Captures properties of MCTP interfaces.
54 *
Thu Nguyen19d1fda2024-12-05 08:43:06 +000055 * https://github.com/CodeConstruct/mctp/blob/v2.0/src/mctp.c#L668-L699
Andrew Jeffery275f7c32024-01-31 12:41:14 +103056 */
57struct MCTPInterface
58{
59 std::string name;
60 MCTPTransport transport;
61
62 auto operator<=>(const MCTPInterface& r) const = default;
63};
64
65class 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 */
75class 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 */
145class 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
176class 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 */
188class 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 */
254class 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
300class 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};