| #include "MctpReactorDevice.hpp" | |
| #include "MctpEndpoint.hpp" | |
| #include "Utils.hpp" | |
| #include <boost/algorithm/string.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 <limits> | |
| #include <memory> | |
| #include <optional> | |
| #include <string_view> | |
| #include <system_error> | |
| #include <variant> | |
| namespace | |
| { | |
| constexpr const char* objectMapperServiceName = | |
| "xyz.openbmc_project.ObjectMapper"; | |
| constexpr const char* configuresObjectPathSuffix = "/configures"; | |
| constexpr const char* dbusPropertiesInterface = | |
| "org.freedesktop.DBus.Properties"; | |
| constexpr const char* associationInterface = "xyz.openbmc_project.Association"; | |
| constexpr const char* endpointPropertyName = "endpoints"; | |
| // Returns the token after the last '/' character. | |
| std::string extractSuffixFromObjectPath(const std::string& path) | |
| { | |
| std::vector<std::string> tokens; | |
| boost::split(tokens, path, boost::is_any_of("/")); | |
| if (tokens.empty()) | |
| { | |
| return ""; | |
| } | |
| return tokens.back(); | |
| } | |
| // Returns a pair consisting of the network number and endpoint ID parsed from | |
| // an MCTP endpoint object path such as | |
| // "/au/com/codeconstruct/mctp1/networks/1/endpoints/17" for example. | |
| // Assumes that the last number is EID and the first number is the network. | |
| std::optional<std::pair<int, uint8_t>> | |
| extractNetworkAndEid(std::string_view endpointObjectPath) | |
| { | |
| std::vector<std::string> tokens; | |
| boost::split(tokens, endpointObjectPath, boost::is_any_of("/")); | |
| auto it = tokens.rbegin(); | |
| // Find EID. | |
| int eid = -1; | |
| for (; it != tokens.rend(); ++it) | |
| { | |
| try | |
| { | |
| eid = std::stoi(*it); | |
| } | |
| catch (...) | |
| { | |
| continue; | |
| } | |
| break; | |
| } | |
| // Make sure the next (preceding) token is "enpdoints". | |
| if (it == tokens.rend() || it + 1 == tokens.rend() || | |
| *(it + 1) != "endpoints") | |
| { | |
| return std::nullopt; | |
| } | |
| // Number validity check. | |
| if (eid < std::numeric_limits<uint8_t>::min() || | |
| eid > std::numeric_limits<uint8_t>::max()) | |
| { | |
| return std::nullopt; | |
| } | |
| // Find network. | |
| int network = -1; | |
| for (++it; it != tokens.rend(); ++it) | |
| { | |
| try | |
| { | |
| network = std::stoi(*it); | |
| } | |
| catch (...) | |
| { | |
| continue; | |
| } | |
| break; | |
| } | |
| // Make sure the next (preceding) token is "networks". | |
| if (it == tokens.rend() || it + 1 == tokens.rend() || | |
| *(it + 1) != "networks") | |
| { | |
| return std::nullopt; | |
| } | |
| // Number validity check. | |
| if (network < 0) | |
| { | |
| return std::nullopt; | |
| } | |
| return std::make_pair(network, eid); | |
| } | |
| } // namespace | |
| MctpReactorDevice::MctpReactorDevice( | |
| const std::shared_ptr<sdbusplus::asio::connection>& connection, | |
| const std::string& deviceObjectPath, std::optional<int> busNumber, | |
| std::optional<int> address) : | |
| connection(connection), deviceObjectPath(deviceObjectPath), | |
| prettyObjectName(extractSuffixFromObjectPath(deviceObjectPath)), | |
| isI2cAccessible(busNumber.has_value() && address.has_value()), | |
| i2cBusNumber(isI2cAccessible ? busNumber.value() : -1), | |
| i2cAddress(isI2cAccessible ? address.value() : -1) | |
| {} | |
| void MctpReactorDevice::setup( | |
| std::function<void(const std::error_code& ec, | |
| const std::shared_ptr<MctpEndpoint>& ep)>&& action) | |
| { | |
| auto onGetPropertyReturned = | |
| [weak{weak_from_this()}, action{std::move(action)}]( | |
| const boost::system::error_code& ec, | |
| const std::variant<std::vector<std::string>>& value) mutable { | |
| if (ec) | |
| { | |
| action(ec, {}); | |
| return; | |
| } | |
| const auto* const endpointObjectPaths = | |
| std::get_if<std::vector<std::string>>(&value); | |
| if (!endpointObjectPaths || endpointObjectPaths->size() != 1) | |
| { | |
| auto errc = std::errc::invalid_argument; | |
| auto ec = std::make_error_code(errc); | |
| action(ec, {}); | |
| return; | |
| } | |
| if (auto self = weak.lock()) | |
| { | |
| self->finaliseEndpoint(endpointObjectPaths->front(), | |
| std::move(action)); | |
| } | |
| }; | |
| try | |
| { | |
| const std::string configuresObjectPath = deviceObjectPath + | |
| configuresObjectPathSuffix; | |
| connection->async_method_call( | |
| onGetPropertyReturned, objectMapperServiceName, | |
| configuresObjectPath, dbusPropertiesInterface, "Get", | |
| std::string(associationInterface), | |
| std::string(endpointPropertyName)); | |
| } | |
| 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 MctpReactorDevice::remove() | |
| { | |
| if (endpoint) | |
| { | |
| endpoint->remove(); | |
| } | |
| } | |
| std::string MctpReactorDevice::describe() const | |
| { | |
| return "MctpReactorDev: " + prettyObjectName; | |
| } | |
| void MctpReactorDevice::onEndpointInterfacesRemoved( | |
| const std::weak_ptr<MctpReactorDevice>& weak, | |
| const std::string& endpointObjectPath, sdbusplus::message_t& msg) | |
| { | |
| auto objectPath = msg.unpack<sdbusplus::message::object_path>(); | |
| if (objectPath.str != endpointObjectPath) | |
| { | |
| return; | |
| } | |
| auto removedInterfaces = msg.unpack<std::set<std::string>>(); | |
| if (!removedInterfaces.contains(mctpdEndpointControlInterface)) | |
| { | |
| return; | |
| } | |
| if (auto self = weak.lock()) | |
| { | |
| self->endpointRemoved(); | |
| } | |
| } | |
| void MctpReactorDevice::finaliseEndpoint( | |
| const std::string& endpointObjectPath, | |
| 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, endpointObjectPath)); | |
| removeMatch = std::make_unique<sdbusplus::bus::match_t>( | |
| *connection, matchSpec, | |
| std::bind_front(MctpReactorDevice::onEndpointInterfacesRemoved, | |
| weak_from_this(), endpointObjectPath)); | |
| const auto networkAndEid = extractNetworkAndEid(endpointObjectPath); | |
| if (!networkAndEid) | |
| { | |
| action(std::make_error_code(std::errc::invalid_argument), nullptr); | |
| return; | |
| } | |
| const auto [network, eid] = networkAndEid.value(); | |
| endpoint = std::make_shared<MctpdEndpoint>(shared_from_this(), connection, | |
| endpointObjectPath, network, eid, | |
| isI2cAccessible); | |
| action({}, endpoint); | |
| } | |
| void MctpReactorDevice::endpointRemoved() | |
| { | |
| if (endpoint) | |
| { | |
| removeMatch.reset(); | |
| endpoint->remove(); | |
| endpoint.reset(); | |
| } | |
| } |