blob: 02322d10cbe9afe274504e12babddc037dcc0056 [file] [log] [blame]
#pragma once
#include "Utils.hpp"
#include <boost/asio/steady_timer.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/message.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <cstdint>
#include <iostream>
/**
* @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<void(const std::shared_ptr<MCTPEndpoint>& ep)>;
using Result = std::function<void(const std::error_code& ec)>;
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<MCTPDevice> 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<void(const std::error_code& ec,
const std::shared_ptr<MCTPEndpoint>& 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<MCTPDEndpoint>
{
public:
static std::string path(const std::shared_ptr<MCTPEndpoint>& ep);
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) :
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<MCTPDevice> 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<MCTPDDevice> dev;
std::shared_ptr<sdbusplus::asio::connection> connection;
sdbusplus::message::object_path objpath;
struct
{
int network;
uint8_t eid;
} mctp;
MCTPEndpoint::Event notifyAvailable;
MCTPEndpoint::Event notifyDegraded;
MCTPEndpoint::Event notifyRemoved;
std::optional<sdbusplus::bus::match_t> 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<MCTPDDevice>
{
public:
MCTPDDevice() = delete;
MCTPDDevice(const std::shared_ptr<sdbusplus::asio::connection>& connection,
const std::string& interface,
const std::vector<uint8_t>& physaddr);
MCTPDDevice(const MCTPDDevice& other) = delete;
MCTPDDevice(MCTPDDevice&& other) = delete;
~MCTPDDevice() override = default;
void setup(std::function<void(const std::error_code& ec,
const std::shared_ptr<MCTPEndpoint>& ep)>&&
added) override;
void remove() override;
std::string describe() const override;
private:
static void onEndpointInterfacesRemoved(
const std::weak_ptr<MCTPDDevice>& weak, const std::string& objpath,
sdbusplus::message_t& msg);
std::shared_ptr<sdbusplus::asio::connection> connection;
const std::string interface;
const std::vector<uint8_t> physaddr;
std::shared_ptr<MCTPDEndpoint> endpoint;
std::unique_ptr<sdbusplus::bus::match_t> 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<void(const std::error_code& ec,
const std::shared_ptr<MCTPEndpoint>& ep)>& added);
void endpointRemoved();
};
class I2CMCTPDDevice : public MCTPDDevice
{
public:
static std::optional<SensorBaseConfigMap> match(const SensorData& config);
static bool match(const std::set<std::string>& interfaces);
static std::shared_ptr<I2CMCTPDDevice>
from(const std::shared_ptr<sdbusplus::asio::connection>& connection,
const SensorBaseConfigMap& iface);
I2CMCTPDDevice() = delete;
I2CMCTPDDevice(
const std::shared_ptr<sdbusplus::asio::connection>& 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);
};