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