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