blob: 50145e0794f94a4f01a1fa722c5fbb618c034a9c [file] [log] [blame]
#include "MctpEndpoint.hpp"
#include "Utils.hpp"
#include <boost/system/detail/errc.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/exception.hpp>
#include <sdbusplus/message.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <exception>
#include <memory>
#include <system_error>
static constexpr const char* mctpdBusName = "xyz.openbmc_project.MCTP";
static constexpr const char* mctpdControlPath = "/xyz/openbmc_project/mctp";
static constexpr const char* mctpdControlInterface =
"au.com.CodeConstruct.MCTP";
static constexpr const char* mctpdEndpointControlInterface =
"au.com.CodeConstruct.MCTP.Endpoint";
MctpdDevice::MctpdDevice(
const std::shared_ptr<sdbusplus::asio::connection>& connection,
const std::string& interface, const std::vector<uint8_t>& physaddr) :
connection(connection), interface(interface), physaddr(physaddr)
{}
void MctpdDevice::onEndpointInterfacesRemoved(
const std::weak_ptr<MctpdDevice>& weak, const std::string& objpath,
sdbusplus::message_t& msg)
{
auto path = msg.unpack<sdbusplus::message::object_path>();
if (path.str != objpath)
{
return;
}
auto removedIfaces = msg.unpack<std::set<std::string>>();
if (!removedIfaces.contains(mctpdEndpointControlInterface))
{
return;
}
if (auto self = weak.lock())
{
self->endpointRemoved();
}
}
void MctpdDevice::finaliseEndpoint(
const std::string& objpath, uint8_t eid, int network,
std::function<void(const std::error_code& ec,
const std::shared_ptr<MctpEndpoint>& ep)>&& action)
{
const auto matchSpec =
std::string(sdbusplus::bus::match::rules::interfacesRemoved())
.append(sdbusplus::bus::match::rules::argNpath(0, objpath));
removeMatch = std::make_unique<sdbusplus::bus::match_t>(
*connection, matchSpec,
std::bind_front(MctpdDevice::onEndpointInterfacesRemoved,
weak_from_this(), objpath));
endpoint = std::make_shared<MctpdEndpoint>(shared_from_this(), connection,
objpath, network, eid);
action({}, endpoint);
}
void MctpdDevice::setup(
std::function<void(const std::error_code& ec,
const std::shared_ptr<MctpEndpoint>& ep)>&& action)
{
auto onSetup = [weak{weak_from_this()}, action{std::move(action)}](
const boost::system::error_code& ec, uint8_t eid,
int network, const std::string& objpath,
bool allocated [[maybe_unused]]) mutable {
if (ec)
{
action(ec, {});
return;
}
if (auto self = weak.lock())
{
self->finaliseEndpoint(objpath, eid, network, std::move(action));
}
};
try
{
connection->async_method_call(onSetup, mctpdBusName, mctpdControlPath,
mctpdControlInterface, "SetupEndpoint",
interface, physaddr);
}
catch (const sdbusplus::exception::SdBusError& err)
{
auto errc = std::errc::no_such_device_or_address;
auto ec = std::make_error_code(errc);
action(ec, {});
}
}
void MctpdDevice::endpointRemoved()
{
if (endpoint)
{
removeMatch.reset();
endpoint->removed();
endpoint.reset();
}
}
void MctpdDevice::remove()
{
if (endpoint)
{
endpoint->remove();
}
}
SmbusMctpdDevice::SmbusMctpdDevice(
const std::shared_ptr<sdbusplus::asio::connection>& connection, int smbus,
uint8_t smdev) :
MctpdDevice(connection, std::string("mctpi2c") + std::to_string(smbus),
{smdev}),
smbus(smbus), smdev(smdev)
{}
std::string SmbusMctpdDevice::describe() const
{
return std::format("bus: {:2}, address: {:#x}", smbus, smdev);
}
MctpdEndpoint::MctpdEndpoint(
const std::shared_ptr<MctpDevice>& device,
const std::shared_ptr<sdbusplus::asio::connection>& connection,
sdbusplus::message::object_path objpath, int network, uint8_t eid) :
device(device), connection(connection), objpath(std::move(objpath)),
mctp{network, eid}
{}
void MctpdEndpoint::onMctpEndpointChange(sdbusplus::message_t& msg)
{
std::string iface;
std::map<std::string, BasicVariantType> changed;
std::vector<std::string> invalidated;
msg.read(iface);
msg.read(changed);
msg.read(invalidated);
if (iface != mctpdEndpointControlInterface)
{
return;
}
auto it = changed.find("Connectivity");
if (it == changed.end())
{
return;
}
updateEndpointConnectivity(std::get<std::string>(it->second));
}
void MctpdEndpoint::updateEndpointConnectivity(const std::string& connectivity)
{
if (connectivity == "Degraded")
{
if (notifyDegraded)
{
notifyDegraded(shared_from_this());
}
}
else if (connectivity == "Available")
{
if (notifyAvailable)
{
notifyAvailable(shared_from_this());
}
}
else
{
std::cerr << "Unrecognised connectivity state: '" << connectivity << "'"
<< '\n';
}
}
int MctpdEndpoint::network() const
{
return mctp.network;
}
uint8_t MctpdEndpoint::eid() const
{
return mctp.eid;
}
void MctpdEndpoint::subscribe(Event&& degraded, Event&& available,
Event&& removed)
{
const auto matchType = std::string("type='signal'");
const auto matchMember = std::string("member='PropertiesChanged'");
const auto pathNamespace = std::string("path_namespace='") + objpath.str +
"'";
const auto arg0Namespace = std::string("arg0namespace='") +
mctpdEndpointControlInterface + "'";
const auto matchSpec = std::string()
.append(matchType)
.append(",")
.append(matchMember)
.append(",")
.append(pathNamespace)
.append(",")
.append(arg0Namespace);
this->notifyDegraded = degraded;
this->notifyAvailable = available;
this->notifyRemoved = removed;
try
{
connectivityMatch.emplace(
static_cast<sdbusplus::bus_t&>(*connection), matchSpec,
[weak{weak_from_this()}](sdbusplus::message_t& msg) {
if (auto self = weak.lock())
{
self->onMctpEndpointChange(msg);
}
});
connection->async_method_call(
[weak{weak_from_this()}](const boost::system::error_code& ec,
const std::variant<std::string>& value) {
if (ec)
{
std::cerr << "Failed to get current connectivity state: " << ec
<< '\n';
return;
}
if (auto self = weak.lock())
{
const std::string& connectivity = std::get<std::string>(value);
self->updateEndpointConnectivity(connectivity);
}
},
mctpdBusName, objpath.str, "org.freedesktop.DBus.Properties", "Get",
mctpdEndpointControlInterface, "Connectivity");
}
catch (const sdbusplus::exception::SdBusError& err)
{
this->notifyDegraded = nullptr;
this->notifyAvailable = nullptr;
this->notifyRemoved = nullptr;
std::throw_with_nested(
MctpException("Failed to register connectivity signal match"));
}
}
void MctpdEndpoint::recover()
{
try
{
connection->async_method_call(
[weak{weak_from_this()}](const boost::system::error_code& ec
[[maybe_unused]]) {
if (ec)
{
if (auto self = weak.lock())
{
std::cerr << "Failed to recover device at '"
<< self->objpath.str << "'" << '\n';
}
}
},
mctpdBusName, objpath.str, mctpdEndpointControlInterface,
"Recover");
}
catch (const sdbusplus::exception::SdBusError& err)
{
std::throw_with_nested(
MctpException("Failed to schedule endpoint recovery"));
}
}
void MctpdEndpoint::remove()
{
try
{
connection->async_method_call(
[self{shared_from_this()}](const boost::system::error_code& ec) {
if (ec)
{
std::cerr << "Failed to remove endpoint [" << self->describe()
<< "]" << '\n';
return;
}
}, mctpdBusName, objpath.str, mctpdEndpointControlInterface, "Remove");
}
catch (const sdbusplus::exception::SdBusError& err)
{
std::throw_with_nested(
MctpException("Failed schedule endpoint removal"));
}
}
void MctpdEndpoint::removed()
{
if (notifyRemoved)
{
notifyRemoved(shared_from_this());
}
}
void MctpdEndpoint::setMtu(
uint32_t mtu, std::function<void(const std::error_code& ec)>&& completed)
{
try
{
connection->async_method_call(
[cb{std::move(completed)}](const boost::system::error_code& bsec) {
cb(static_cast<const std::error_code&>(bsec));
}, mctpdBusName, objpath.str, mctpdEndpointControlInterface, "SetMTU",
mtu);
}
catch (const sdbusplus::exception::SdBusError& err)
{
completed(std::error_code(err.get_errno(), std::system_category()));
}
}
std::string MctpdEndpoint::describe() const
{
return std::string("network: ")
.append(std::to_string(mctp.network))
.append(", EID: ")
.append(std::to_string(mctp.eid))
.append(" | ")
.append(device->describe());
}