blob: 1d072e906ed13411adbe6fa7c49f5f66751c7ea8 [file] [log] [blame]
#include "rde_match_handler.hpp"
#include "util/mctp_setup.hpp"
#include <sdbusplus/unpack_properties.hpp>
#include <stdplus/print.hpp>
#include <format>
using DbusVariant = std::variant<std::string, uint64_t, uint32_t, uint16_t,
uint8_t, sdbusplus::message::object_path>;
using ChangedPropertiesType = std::vector<std::pair<std::string, DbusVariant>>;
RdeMatchHandler::RdeMatchHandler(int socketFd) : fd(socketFd)
{
pldmInterface = std::make_shared<PldmInterface>();
}
RdeMatchHandler::~RdeMatchHandler()
{}
int RdeMatchHandler::initiateDiscovery(const int& fd, const std::string& udevId,
const int& netId)
{
// Begin Base Discovery
stdplus::print(stderr, "Initiating PLDM Discovery...\n");
std::unique_ptr<BaseDiscoveryStateMachine> stateMachineBase =
std::make_unique<BaseDiscoveryStateMachine>(fd, udevId, netId,
pldmInterface);
int rc = 0;
for (int retryCounter = 0; retryCounter < MAX_RETRIES_MCTP_SOCK_FAILURE;
retryCounter++)
{
rc = static_cast<int>(stateMachineBase->run());
if (rc == 0)
{
break; // Successfully completed state machine run
}
if (retryCounter == (MAX_RETRIES_MCTP_SOCK_FAILURE - 1))
{
stdplus::println(stderr,
"Initial base discovery failed with all retries.");
return rc;
}
stdplus::println(stderr,
"Initial Base Discovery failed."
"Retrying with retryCounter: {}",
(retryCounter + 1));
}
this->deviceToBaseStateMap.emplace(udevId, std::move(stateMachineBase));
if constexpr (DEBUG)
{
this->deviceToBaseStateMap[udevId]->printState();
}
std::unique_ptr<RdeDiscoveryStateMachine> stateMachineRde =
std::make_unique<RdeDiscoveryStateMachine>(fd, udevId, netId,
pldmInterface);
for (int retryCounter = 0; retryCounter < MAX_RETRIES_MCTP_SOCK_FAILURE;
retryCounter++)
{
rc = static_cast<int>(stateMachineRde->run());
if (rc == 0)
{
break; // Successfully completed state machine run
}
if (retryCounter == (MAX_RETRIES_MCTP_SOCK_FAILURE - 1))
{
stdplus::println(stderr,
"Initial RDE discovery failed with all retries.");
return rc;
}
stdplus::println(stderr,
"Initial RDE discovery failed."
"Retrying with retryCounter: {}",
(retryCounter + 1));
}
this->deviceToRdeStateMap.emplace(udevId, std::move(stateMachineRde));
if constexpr (DEBUG)
{
this->deviceToRdeStateMap[udevId]->printState();
}
return rc;
}
void RdeMatchHandler::scanForExistingDevices(
const int& fd, sdbusplus::asio::object_server& objectServer,
const std::string& prefixPath,
std::shared_ptr<sdbusplus::asio::connection>& systemBus)
{
using propertyMap =
std::vector<std::pair<std::string, std::vector<std::string>>>;
constexpr std::array<std::string_view, 1> interfaces = {
"xyz.openbmc_project.Configuration.RdeSatelliteController"};
using DBusProperties = std::vector<std::pair<std::string, DbusVariant>>;
systemBus->async_method_call_timed(
[this, &fd, &systemBus, &objectServer, &prefixPath](
const boost::system::error_code& ec,
const std::vector<std::pair<std::string, propertyMap>>& subtree) {
if (ec)
{
stdplus::print(
stderr,
"Failed to scan existing RDE object paths with error msg: {}\n",
ec.message());
return;
}
if (subtree.empty())
{
stdplus::print(
stderr,
"RDE Device scan empty. "
"No instances found while parsing Entity Manager tree\n");
return;
}
for (const auto& [objPath, services] : subtree)
{
for (const auto& service : services)
{
systemBus->async_method_call_timed(
[this, &fd, &objectServer, &prefixPath,
objPath](const boost::system::error_code& errorCode,
const DBusProperties& properties) {
if (errorCode)
{
stdplus::print(
stderr,
"Scanning RDE Devices failed."
" Error in DBus response ec: {} and msg: {}\n",
errorCode.value(), errorCode.message());
return;
}
std::string vendorId;
std::string udevId;
std::string port;
const bool success = sdbusplus::unpackPropertiesNoThrow(
[](const sdbusplus::UnpackErrorReason reason,
const std::string& property) {
stdplus::print(
stderr,
"Error unpacking the property: {} with error: {}\n",
property, static_cast<int>(reason));
},
properties, "VID", vendorId, "USBPORT", port, "UDEVID",
udevId);
if (success)
{
if (vendorId.empty() || port.empty() || udevId.empty())
{
stdplus::print(
stderr,
"RDE Device scan failed due to matcher not"
"having the required properties\n");
return;
}
// Do PLDM Setup
rdeSetup(fd, objectServer, std::string(objPath), port,
udevId, vendorId, prefixPath);
}
else
{
stdplus::print(
stderr,
"RDE device scan failed due to error in parsing"
"properties\n");
}
},
service.first, objPath, "org.freedesktop.DBus.Properties",
"GetAll", /*timeout in usec*/ 90000000,
"xyz.openbmc_project.Configuration.RdeSatelliteController");
}
}
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
/*timeout in usec*/ 90000000, "/xyz/openbmc_project/inventory", 0,
interfaces);
}
std::unique_ptr<sdbusplus::bus::match_t> RdeMatchHandler::handleMatchAdd(
std::shared_ptr<sdbusplus::asio::connection>& systemBus,
sdbusplus::asio::object_server& objectServer, const std::string& prefixPath,
const int& fd)
{
return std::make_unique<sdbusplus::bus::match_t>(
*systemBus, sdbusplus::bus::match::rules::interfacesAdded(),
[this, &objectServer, &prefixPath, &systemBus,
&fd](sdbusplus::message_t& reply) {
sdbusplus::message::object_path changedObject;
reply.read(changedObject);
std::vector<std::pair<std::string, ChangedPropertiesType>>
changedInterfaces;
reply.read(changedInterfaces);
std::string vendorId;
std::string udevId;
std::string port;
for (const auto& [changedInterface, changedProps] : changedInterfaces)
{
if (changedInterface !=
"xyz.openbmc_project.Configuration.RdeSatelliteController")
{
continue;
}
if constexpr (DEBUG)
{
stdplus::print(stderr, "New device detected: {}\n",
std::string(changedObject));
}
for (auto& [key, value] : changedProps)
{
if (key == "VID")
{
vendorId = std::get<std::string>(value);
}
else if (key == "USBPORT")
{
port = std::get<std::string>(value);
}
else if (key == "UDEVID")
{
udevId = std::get<std::string>(value);
}
}
if (vendorId.empty() || port.empty() || udevId.empty())
{
stdplus::print(stderr,
"Matcher does not have required properties\n");
return;
}
rdeSetup(fd, objectServer, std::string(changedObject), port, udevId,
vendorId, prefixPath);
}
});
}
std::unique_ptr<sdbusplus::bus::match_t> RdeMatchHandler::handleMatchRemove(
std::shared_ptr<sdbusplus::asio::connection>& systemBus,
sdbusplus::asio::object_server& objectServer)
{
return std::make_unique<sdbusplus::bus::match_t>(
*systemBus, sdbusplus::bus::match::rules::interfacesRemoved(),
[this, &objectServer](sdbusplus::message_t& reply) {
sdbusplus::message::object_path changedObject;
std::vector<std::string> interfacesRemoved;
reply.read(changedObject, interfacesRemoved);
auto it = deviceToDbusIntfMap.find(std::string(changedObject));
if (it != deviceToDbusIntfMap.end())
{
std::shared_ptr<sdbusplus::asio::dbus_interface> iface = it->second;
auto removed = objectServer.remove_interface(iface);
stdplus::print(stderr, "Removed RDE Device from tree: {}\n",
removed);
deviceToDbusIntfMap.erase(std::string(changedObject));
auto it = objectPathToDeviceIdMap.find(std::string(changedObject));
if (it != objectPathToDeviceIdMap.end())
{
cleanupMctpLink(it->second);
cleanupDiscoveryStates(it->second);
// TODO (@harshtya): clean up the dictionaries
objectPathToDeviceIdMap.erase(std::string(changedObject));
deviceToNetIdMap.erase(it->second);
}
}
});
}
void RdeMatchHandler::triggerMatcher()
{
boost::asio::io_context io;
std::shared_ptr<sdbusplus::asio::connection> systemBus =
std::make_shared<sdbusplus::asio::connection>(io);
sdbusplus::asio::object_server objectServer(systemBus, true);
objectServer.add_manager("/xyz/openbmc_project/rde_devices");
std::string prefixPath = "/xyz/openbmc_project/rde_devices/";
try
{
scanForExistingDevices(this->fd, objectServer, prefixPath, systemBus);
}
catch (const std::exception& e)
{
// skip scan and wait for new device detection
stdplus::println(
stderr,
"Some exception occured while scanning for existing rde devices {}",
e.what());
}
// Initialize the match handler for interfacesAdded
auto matchAdd =
handleMatchAdd(systemBus, objectServer, prefixPath, this->fd);
// Matcher to monitor for devices removed
auto matchRemove = handleMatchRemove(systemBus, objectServer);
systemBus->request_name("xyz.openbmc_project.rdeoperation");
io.run();
}
int RdeMatchHandler::rdeSetup(const int& fd,
sdbusplus::asio::object_server& objectServer,
const std::string& changedObject,
const std::string& port,
const std::string& udevId,
const std::string& vendorId,
const std::string& prefixPath)
{
int netId = setupOnePort(port, udevId); // MCTP Setup
deviceToNetIdMap.emplace(udevId, netId);
int rc = initiateDiscovery(fd, udevId, netId);
if (rc != 0)
{
stdplus::print(stderr,
"PLDM/RDE Discovery failed for device: {}"
"and udevId: {} after all retries\n",
port, udevId);
return rc;
}
std::string objectPath = std::format("{}{}", prefixPath, udevId);
if constexpr (DEBUG)
{
stdplus::print(stderr, "Creating object path for RDE Operation: {}\n ",
objectPath);
}
std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
objectServer.add_interface(objectPath, "xyz.openbmc_project.RdeDevice");
iface->register_property("VID", vendorId,
sdbusplus::asio::PropertyPermission::readOnly);
iface->register_property("USBPORT", port,
sdbusplus::asio::PropertyPermission::readOnly);
iface->register_property("UDEVID", udevId,
sdbusplus::asio::PropertyPermission::readOnly);
// TODO(@harshtya): Add RDE Operation Handler Dbus method
iface->initialize();
deviceToDbusIntfMap.emplace(std::string(changedObject), iface);
objectPathToDeviceIdMap.emplace(std::string(changedObject), udevId);
return 0;
}
void RdeMatchHandler::cleanupDiscoveryStates(const std::string& udevId)
{
// Remove Base discovery state if exists
auto it = deviceToBaseStateMap.find(udevId);
if (it != deviceToBaseStateMap.end())
{
deviceToBaseStateMap.erase(udevId);
}
// Remove RDE discovery state if exists
it = deviceToRdeStateMap.find(udevId);
if (it != deviceToRdeStateMap.end())
{
deviceToRdeStateMap.erase(udevId);
}
}