#pragma once #include "Utils.hpp" #include #include #include #include #include #include #include /** * @file * @brief Abstract and concrete classes representing MCTP concepts and * behaviours. */ /** * @brief An exception type that may be thrown by implementations of the MCTP * abstract classes. * * This exception should be the basis for all exceptions thrown out of the MCTP * APIs, and should capture any other exceptions that occur. */ class MCTPException : public std::exception { public: MCTPException() = delete; explicit MCTPException(const char* desc) : desc(desc) {} const char* what() const noexcept override { return desc; } private: const char* desc; }; /** * @brief An enum of the MCTP transports described in DSP0239 v1.10.0 Section 7. * * https://www.dmtf.org/sites/default/files/standards/documents/DSP0239_1.10.0.pdf */ enum class MCTPTransport { Reserved = 0x00, SMBus = 0x01, }; /** * @brief Captures properties of MCTP interfaces. * * https://github.com/CodeConstruct/mctp/blob/v1.1/src/mctp.c#L672-L703 */ struct MCTPInterface { std::string name; MCTPTransport transport; auto operator<=>(const MCTPInterface& r) const = default; }; class MCTPDevice; /** * @brief Captures the behaviour of an endpoint at the MCTP layer * * The lifetime of an instance of MctpEndpoint is proportional to the lifetime * of the endpoint configuration. If an endpoint is deconfigured such that its * device has no assigned EID, then any related MctpEndpoint instance must be * destructed as a consequence. */ class MCTPEndpoint { public: using Event = std::function& ep)>; using Result = std::function; virtual ~MCTPEndpoint() = default; /** * @return The Linux network ID of the network in which the endpoint participates */ virtual int network() const = 0; /** * @return The MCTP endpoint ID of the endpoint in its network */ virtual uint8_t eid() const = 0; /** * @brief Subscribe to events produced by an endpoint object across its * lifecycle * * @param degraded The callback to execute when the MCTP layer indicates the * endpoint is unresponsive * * @param available The callback to execute when the MCTP layer indicates * that communication with the degraded endpoint has been * recovered * * @param removed The callback to execute when the MCTP layer indicates the * endpoint has been removed. */ virtual void subscribe(Event&& degraded, Event&& available, Event&& removed) = 0; /** * @brief Remove the endpoint from its associated network */ virtual void remove() = 0; /** * @return A formatted string representing the endpoint in terms of its * address properties */ virtual std::string describe() const = 0; /** * @return A shared pointer to the device instance associated with the * endpoint. */ virtual std::shared_ptr device() const = 0; }; /** * @brief Represents an MCTP-capable device on a bus. * * It is often known that an MCTP-capable device exists on a bus prior to the * MCTP stack configuring the device for communication. MctpDevice exposes the * ability to set-up the endpoint device for communication. * * The lifetime of an MctpDevice instance is proportional to the existence of an * MCTP-capable device in the system. If a device represented by an MctpDevice * instance is removed from the system then any related MctpDevice instance must * be destructed a consequence. * * Successful set-up of the device as an endpoint yields an MctpEndpoint * instance. The lifetime of the MctpEndpoint instance produced must not exceed * the lifetime of its parent MctpDevice. */ class MCTPDevice { public: virtual ~MCTPDevice() = default; /** * @brief Configure the device for MCTP communication * * @param added The callback to invoke once the setup process has * completed. The provided error code @p ec must be * checked as the request may not have succeeded. If * the request was successful then @p ep contains a * valid MctpEndpoint instance. */ virtual void setup(std::function& ep)>&& added) = 0; /** * @brief Remove the device and any associated endpoint from the MCTP stack. */ virtual void remove() = 0; /** * @return A formatted string representing the device in terms of its * address properties. */ virtual std::string describe() const = 0; }; class MCTPDDevice; /** * @brief An implementation of MctpEndpoint in terms of the D-Bus interfaces * exposed by @c mctpd. * * The lifetime of an MctpdEndpoint is proportional to the lifetime of the * endpoint object exposed by @c mctpd. The lifecycle of @c mctpd endpoint * objects is discussed here: * * https://github.com/CodeConstruct/mctp/pull/23/files#diff-00234f5f2543b8b9b8a419597e55121fe1cc57cf1c7e4ff9472bed83096bd28e */ class MCTPDEndpoint : public MCTPEndpoint, public std::enable_shared_from_this { public: static std::string path(const std::shared_ptr& ep); MCTPDEndpoint() = delete; MCTPDEndpoint( const std::shared_ptr& dev, const std::shared_ptr& connection, sdbusplus::message::object_path objpath, int network, uint8_t eid) : dev(dev), connection(connection), objpath(std::move(objpath)), mctp{network, eid} {} MCTPDEndpoint& McptdEndpoint(const MCTPDEndpoint& other) = delete; MCTPDEndpoint(MCTPDEndpoint&& other) noexcept = default; ~MCTPDEndpoint() override = default; int network() const override; uint8_t eid() const override; void subscribe(Event&& degraded, Event&& available, Event&& removed) override; void remove() override; std::string describe() const override; std::shared_ptr device() const override; /** * @brief Indicate the endpoint has been removed * * Called from the implementation of MctpdDevice for resource cleanup * prior to destruction. Resource cleanup is delegated by invoking the * notifyRemoved() callback. As the actions may be abitrary we avoid * invoking notifyRemoved() in the destructor. */ void removed(); private: std::shared_ptr dev; std::shared_ptr connection; sdbusplus::message::object_path objpath; struct { int network; uint8_t eid; } mctp; MCTPEndpoint::Event notifyAvailable; MCTPEndpoint::Event notifyDegraded; MCTPEndpoint::Event notifyRemoved; std::optional connectivityMatch; void onMctpEndpointChange(sdbusplus::message_t& msg); void updateEndpointConnectivity(const std::string& connectivity); }; /** * @brief An implementation of MctpDevice in terms of D-Bus interfaces exposed * by @c mctpd. * * The construction or destruction of an MctpdDevice is not required to be * correlated with signals from @c mctpd. For instance, EntityManager may expose * the existance of an MCTP-capable device through its usual configuration * mechanisms. */ class MCTPDDevice : public MCTPDevice, public std::enable_shared_from_this { public: MCTPDDevice() = delete; MCTPDDevice(const std::shared_ptr& connection, const std::string& interface, const std::vector& physaddr); MCTPDDevice(const MCTPDDevice& other) = delete; MCTPDDevice(MCTPDDevice&& other) = delete; ~MCTPDDevice() override = default; void setup(std::function& ep)>&& added) override; void remove() override; std::string describe() const override; private: static void onEndpointInterfacesRemoved( const std::weak_ptr& weak, const std::string& objpath, sdbusplus::message_t& msg); std::shared_ptr connection; const std::string interface; const std::vector physaddr; std::shared_ptr endpoint; std::unique_ptr removeMatch; /** * @brief Actions to perform once endpoint setup has succeeded * * Now that the endpoint exists two tasks remain: * * 1. Setup the match capturing removal of the endpoint object by mctpd * 2. Invoke the callback to notify the requester that setup has completed, * providing the MctpEndpoint instance associated with the MctpDevice. */ void finaliseEndpoint( const std::string& objpath, uint8_t eid, int network, std::function& ep)>& added); void endpointRemoved(); }; class I2CMCTPDDevice : public MCTPDDevice { public: static std::optional match(const SensorData& config); static bool match(const std::set& interfaces); static std::shared_ptr from(const std::shared_ptr& connection, const SensorBaseConfigMap& iface); I2CMCTPDDevice() = delete; I2CMCTPDDevice( const std::shared_ptr& connection, int bus, uint8_t physaddr) : MCTPDDevice(connection, interfaceFromBus(bus), {physaddr}) {} ~I2CMCTPDDevice() override = default; private: static constexpr const char* configType = "MCTPI2CTarget"; static std::string interfaceFromBus(int bus); };