| #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); |
| } |
| } |