xref: /openbmc/dbus-sensors/src/mctp/MCTPEndpoint.cpp (revision 19d1fda609a4c39caca73858c90ebee823028362)
1275f7c39SAndrew Jeffery #include "MCTPEndpoint.hpp"
2275f7c39SAndrew Jeffery 
3275f7c39SAndrew Jeffery #include "Utils.hpp"
4275f7c39SAndrew Jeffery #include "VariantVisitors.hpp"
5275f7c39SAndrew Jeffery 
6275f7c39SAndrew Jeffery #include <bits/fs_dir.h>
7275f7c39SAndrew Jeffery 
8275f7c39SAndrew Jeffery #include <boost/system/detail/errc.hpp>
9275f7c39SAndrew Jeffery #include <phosphor-logging/lg2.hpp>
10275f7c39SAndrew Jeffery #include <sdbusplus/asio/connection.hpp>
11275f7c39SAndrew Jeffery #include <sdbusplus/bus.hpp>
12275f7c39SAndrew Jeffery #include <sdbusplus/bus/match.hpp>
13275f7c39SAndrew Jeffery #include <sdbusplus/exception.hpp>
14275f7c39SAndrew Jeffery #include <sdbusplus/message.hpp>
15275f7c39SAndrew Jeffery #include <sdbusplus/message/native_types.hpp>
16275f7c39SAndrew Jeffery 
17275f7c39SAndrew Jeffery #include <cassert>
18275f7c39SAndrew Jeffery #include <charconv>
19275f7c39SAndrew Jeffery #include <cstdint>
20275f7c39SAndrew Jeffery #include <exception>
21275f7c39SAndrew Jeffery #include <filesystem>
22275f7c39SAndrew Jeffery #include <format>
23275f7c39SAndrew Jeffery #include <functional>
24275f7c39SAndrew Jeffery #include <map>
25275f7c39SAndrew Jeffery #include <memory>
26275f7c39SAndrew Jeffery #include <optional>
27275f7c39SAndrew Jeffery #include <set>
28275f7c39SAndrew Jeffery #include <stdexcept>
29275f7c39SAndrew Jeffery #include <string>
30275f7c39SAndrew Jeffery #include <system_error>
31275f7c39SAndrew Jeffery #include <utility>
32275f7c39SAndrew Jeffery #include <variant>
33275f7c39SAndrew Jeffery #include <vector>
34275f7c39SAndrew Jeffery 
35275f7c39SAndrew Jeffery PHOSPHOR_LOG2_USING;
36275f7c39SAndrew Jeffery 
37*19d1fda6SThu Nguyen static constexpr const char* mctpdBusName = "au.com.codeconstruct.MCTP1";
38*19d1fda6SThu Nguyen static constexpr const char* mctpdControlPath = "/au/com/codeconstruct/mctp1";
39275f7c39SAndrew Jeffery static constexpr const char* mctpdControlInterface =
40*19d1fda6SThu Nguyen     "au.com.codeconstruct.MCTP.BusOwner1";
41275f7c39SAndrew Jeffery static constexpr const char* mctpdEndpointControlInterface =
42*19d1fda6SThu Nguyen     "au.com.codeconstruct.MCTP.Endpoint1";
43275f7c39SAndrew Jeffery 
MCTPDDevice(const std::shared_ptr<sdbusplus::asio::connection> & connection,const std::string & interface,const std::vector<uint8_t> & physaddr)44275f7c39SAndrew Jeffery MCTPDDevice::MCTPDDevice(
45275f7c39SAndrew Jeffery     const std::shared_ptr<sdbusplus::asio::connection>& connection,
46275f7c39SAndrew Jeffery     const std::string& interface, const std::vector<uint8_t>& physaddr) :
47275f7c39SAndrew Jeffery     connection(connection), interface(interface), physaddr(physaddr)
48275f7c39SAndrew Jeffery {}
49275f7c39SAndrew Jeffery 
onEndpointInterfacesRemoved(const std::weak_ptr<MCTPDDevice> & weak,const std::string & objpath,sdbusplus::message_t & msg)50275f7c39SAndrew Jeffery void MCTPDDevice::onEndpointInterfacesRemoved(
51275f7c39SAndrew Jeffery     const std::weak_ptr<MCTPDDevice>& weak, const std::string& objpath,
52275f7c39SAndrew Jeffery     sdbusplus::message_t& msg)
53275f7c39SAndrew Jeffery {
54275f7c39SAndrew Jeffery     auto path = msg.unpack<sdbusplus::message::object_path>();
55275f7c39SAndrew Jeffery     assert(path.str == objpath);
56275f7c39SAndrew Jeffery 
57275f7c39SAndrew Jeffery     auto removedIfaces = msg.unpack<std::set<std::string>>();
58275f7c39SAndrew Jeffery     if (!removedIfaces.contains(mctpdEndpointControlInterface))
59275f7c39SAndrew Jeffery     {
60275f7c39SAndrew Jeffery         return;
61275f7c39SAndrew Jeffery     }
62275f7c39SAndrew Jeffery 
63275f7c39SAndrew Jeffery     if (auto self = weak.lock())
64275f7c39SAndrew Jeffery     {
65275f7c39SAndrew Jeffery         self->endpointRemoved();
66275f7c39SAndrew Jeffery     }
67275f7c39SAndrew Jeffery     else
68275f7c39SAndrew Jeffery     {
69275f7c39SAndrew Jeffery         info(
70275f7c39SAndrew Jeffery             "Device for inventory at '{INVENTORY_PATH}' was destroyed concurrent to endpoint removal",
71275f7c39SAndrew Jeffery             "INVENTORY_PATH", objpath);
72275f7c39SAndrew Jeffery     }
73275f7c39SAndrew Jeffery }
74275f7c39SAndrew Jeffery 
finaliseEndpoint(const std::string & objpath,uint8_t eid,int network,std::function<void (const std::error_code & ec,const std::shared_ptr<MCTPEndpoint> & ep)> & added)75275f7c39SAndrew Jeffery void MCTPDDevice::finaliseEndpoint(
76275f7c39SAndrew Jeffery     const std::string& objpath, uint8_t eid, int network,
77275f7c39SAndrew Jeffery     std::function<void(const std::error_code& ec,
78275f7c39SAndrew Jeffery                        const std::shared_ptr<MCTPEndpoint>& ep)>& added)
79275f7c39SAndrew Jeffery {
80275f7c39SAndrew Jeffery     const auto matchSpec =
81275f7c39SAndrew Jeffery         sdbusplus::bus::match::rules::interfacesRemovedAtPath(objpath);
82275f7c39SAndrew Jeffery     removeMatch = std::make_unique<sdbusplus::bus::match_t>(
83275f7c39SAndrew Jeffery         *connection, matchSpec,
84275f7c39SAndrew Jeffery         std::bind_front(MCTPDDevice::onEndpointInterfacesRemoved,
85275f7c39SAndrew Jeffery                         weak_from_this(), objpath));
86275f7c39SAndrew Jeffery     endpoint = std::make_shared<MCTPDEndpoint>(shared_from_this(), connection,
87275f7c39SAndrew Jeffery                                                objpath, network, eid);
88275f7c39SAndrew Jeffery     added({}, endpoint);
89275f7c39SAndrew Jeffery }
90275f7c39SAndrew Jeffery 
setup(std::function<void (const std::error_code & ec,const std::shared_ptr<MCTPEndpoint> & ep)> && added)91275f7c39SAndrew Jeffery void MCTPDDevice::setup(
92275f7c39SAndrew Jeffery     std::function<void(const std::error_code& ec,
93275f7c39SAndrew Jeffery                        const std::shared_ptr<MCTPEndpoint>& ep)>&& added)
94275f7c39SAndrew Jeffery {
95275f7c39SAndrew Jeffery     // Use a lambda to separate state validation from business logic,
96275f7c39SAndrew Jeffery     // where the business logic for a successful setup() is encoded in
97275f7c39SAndrew Jeffery     // MctpdDevice::finaliseEndpoint()
98275f7c39SAndrew Jeffery     auto onSetup = [weak{weak_from_this()}, added{std::move(added)}](
99275f7c39SAndrew Jeffery                        const boost::system::error_code& ec, uint8_t eid,
100275f7c39SAndrew Jeffery                        int network, const std::string& objpath,
101275f7c39SAndrew Jeffery                        bool allocated [[maybe_unused]]) mutable {
102275f7c39SAndrew Jeffery         if (ec)
103275f7c39SAndrew Jeffery         {
104275f7c39SAndrew Jeffery             added(ec, {});
105275f7c39SAndrew Jeffery             return;
106275f7c39SAndrew Jeffery         }
107275f7c39SAndrew Jeffery 
108275f7c39SAndrew Jeffery         if (auto self = weak.lock())
109275f7c39SAndrew Jeffery         {
110275f7c39SAndrew Jeffery             self->finaliseEndpoint(objpath, eid, network, added);
111275f7c39SAndrew Jeffery         }
112275f7c39SAndrew Jeffery         else
113275f7c39SAndrew Jeffery         {
114275f7c39SAndrew Jeffery             info(
115275f7c39SAndrew Jeffery                 "Device object for inventory at '{INVENTORY_PATH}' was destroyed concurrent to completion of its endpoint setup",
116275f7c39SAndrew Jeffery                 "INVENTORY_PATH", objpath);
117275f7c39SAndrew Jeffery         }
118275f7c39SAndrew Jeffery     };
119*19d1fda6SThu Nguyen     connection->async_method_call(
120*19d1fda6SThu Nguyen         onSetup, mctpdBusName,
121*19d1fda6SThu Nguyen         mctpdControlPath + std::string("/interfaces/") + interface,
122*19d1fda6SThu Nguyen         mctpdControlInterface, "AssignEndpoint", physaddr);
123275f7c39SAndrew Jeffery }
endpointRemoved()124275f7c39SAndrew Jeffery 
125275f7c39SAndrew Jeffery void MCTPDDevice::endpointRemoved()
126275f7c39SAndrew Jeffery {
127275f7c39SAndrew Jeffery     if (endpoint)
128275f7c39SAndrew Jeffery     {
129275f7c39SAndrew Jeffery         debug("Endpoint removed @ [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT",
130275f7c39SAndrew Jeffery               endpoint->describe());
131275f7c39SAndrew Jeffery         removeMatch.reset();
132275f7c39SAndrew Jeffery         endpoint->removed();
133275f7c39SAndrew Jeffery         endpoint.reset();
134275f7c39SAndrew Jeffery     }
135275f7c39SAndrew Jeffery }
remove()136275f7c39SAndrew Jeffery 
137275f7c39SAndrew Jeffery void MCTPDDevice::remove()
138275f7c39SAndrew Jeffery {
139275f7c39SAndrew Jeffery     if (endpoint)
140275f7c39SAndrew Jeffery     {
141275f7c39SAndrew Jeffery         debug("Removing endpoint @ [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT",
142275f7c39SAndrew Jeffery               endpoint->describe());
143275f7c39SAndrew Jeffery         endpoint->remove();
144275f7c39SAndrew Jeffery     }
145275f7c39SAndrew Jeffery }
describe() const146275f7c39SAndrew Jeffery 
147275f7c39SAndrew Jeffery std::string MCTPDDevice::describe() const
148275f7c39SAndrew Jeffery {
149275f7c39SAndrew Jeffery     std::string description = std::format("interface: {}", interface);
150275f7c39SAndrew Jeffery     if (!physaddr.empty())
151275f7c39SAndrew Jeffery     {
152275f7c39SAndrew Jeffery         description.append(", address: 0x [ ");
153275f7c39SAndrew Jeffery         auto it = physaddr.begin();
154275f7c39SAndrew Jeffery         for (; it != physaddr.end() - 1; it++)
155275f7c39SAndrew Jeffery         {
156275f7c39SAndrew Jeffery             description.append(std::format("{:02x} ", *it));
157275f7c39SAndrew Jeffery         }
158275f7c39SAndrew Jeffery         description.append(std::format("{:02x} ]", *it));
159275f7c39SAndrew Jeffery     }
160275f7c39SAndrew Jeffery     return description;
161275f7c39SAndrew Jeffery }
path(const std::shared_ptr<MCTPEndpoint> & ep)162275f7c39SAndrew Jeffery 
163275f7c39SAndrew Jeffery std::string MCTPDEndpoint::path(const std::shared_ptr<MCTPEndpoint>& ep)
164275f7c39SAndrew Jeffery {
165*19d1fda6SThu Nguyen     return std::format("{}/networks/{}/endpoints/{}", mctpdControlPath,
166*19d1fda6SThu Nguyen                        ep->network(), ep->eid());
167275f7c39SAndrew Jeffery }
onMctpEndpointChange(sdbusplus::message_t & msg)168275f7c39SAndrew Jeffery 
169275f7c39SAndrew Jeffery void MCTPDEndpoint::onMctpEndpointChange(sdbusplus::message_t& msg)
170275f7c39SAndrew Jeffery {
171275f7c39SAndrew Jeffery     auto [iface, changed, _] =
172275f7c39SAndrew Jeffery         msg.unpack<std::string, std::map<std::string, BasicVariantType>,
173275f7c39SAndrew Jeffery                    std::vector<std::string>>();
174275f7c39SAndrew Jeffery     if (iface != mctpdEndpointControlInterface)
175275f7c39SAndrew Jeffery     {
176275f7c39SAndrew Jeffery         return;
177275f7c39SAndrew Jeffery     }
178275f7c39SAndrew Jeffery 
179275f7c39SAndrew Jeffery     auto it = changed.find("Connectivity");
180275f7c39SAndrew Jeffery     if (it == changed.end())
181275f7c39SAndrew Jeffery     {
182275f7c39SAndrew Jeffery         return;
183275f7c39SAndrew Jeffery     }
184275f7c39SAndrew Jeffery 
185275f7c39SAndrew Jeffery     updateEndpointConnectivity(std::get<std::string>(it->second));
186275f7c39SAndrew Jeffery }
updateEndpointConnectivity(const std::string & connectivity)187275f7c39SAndrew Jeffery 
188275f7c39SAndrew Jeffery void MCTPDEndpoint::updateEndpointConnectivity(const std::string& connectivity)
189275f7c39SAndrew Jeffery {
190275f7c39SAndrew Jeffery     if (connectivity == "Degraded")
191275f7c39SAndrew Jeffery     {
192275f7c39SAndrew Jeffery         if (notifyDegraded)
193275f7c39SAndrew Jeffery         {
194275f7c39SAndrew Jeffery             notifyDegraded(shared_from_this());
195275f7c39SAndrew Jeffery         }
196275f7c39SAndrew Jeffery     }
197275f7c39SAndrew Jeffery     else if (connectivity == "Available")
198275f7c39SAndrew Jeffery     {
199275f7c39SAndrew Jeffery         if (notifyAvailable)
200275f7c39SAndrew Jeffery         {
201275f7c39SAndrew Jeffery             notifyAvailable(shared_from_this());
202275f7c39SAndrew Jeffery         }
203275f7c39SAndrew Jeffery     }
204275f7c39SAndrew Jeffery     else
205275f7c39SAndrew Jeffery     {
206275f7c39SAndrew Jeffery         debug("Unrecognised connectivity state: '{CONNECTIVITY_STATE}'",
207275f7c39SAndrew Jeffery               "CONNECTIVITY_STATE", connectivity);
208275f7c39SAndrew Jeffery     }
209275f7c39SAndrew Jeffery }
network() const210275f7c39SAndrew Jeffery 
211275f7c39SAndrew Jeffery int MCTPDEndpoint::network() const
212275f7c39SAndrew Jeffery {
213275f7c39SAndrew Jeffery     return mctp.network;
214275f7c39SAndrew Jeffery }
eid() const215275f7c39SAndrew Jeffery 
216275f7c39SAndrew Jeffery uint8_t MCTPDEndpoint::eid() const
217275f7c39SAndrew Jeffery {
218275f7c39SAndrew Jeffery     return mctp.eid;
219275f7c39SAndrew Jeffery }
subscribe(Event && degraded,Event && available,Event && removed)220275f7c39SAndrew Jeffery 
221275f7c39SAndrew Jeffery void MCTPDEndpoint::subscribe(Event&& degraded, Event&& available,
222275f7c39SAndrew Jeffery                               Event&& removed)
223275f7c39SAndrew Jeffery {
224275f7c39SAndrew Jeffery     const auto matchSpec =
225275f7c39SAndrew Jeffery         sdbusplus::bus::match::rules::propertiesChangedNamespace(
226275f7c39SAndrew Jeffery             objpath.str, mctpdEndpointControlInterface);
227275f7c39SAndrew Jeffery 
228275f7c39SAndrew Jeffery     this->notifyDegraded = std::move(degraded);
229275f7c39SAndrew Jeffery     this->notifyAvailable = std::move(available);
230275f7c39SAndrew Jeffery     this->notifyRemoved = std::move(removed);
231275f7c39SAndrew Jeffery 
232275f7c39SAndrew Jeffery     try
233275f7c39SAndrew Jeffery     {
234275f7c39SAndrew Jeffery         connectivityMatch.emplace(
235275f7c39SAndrew Jeffery             static_cast<sdbusplus::bus_t&>(*connection), matchSpec,
236275f7c39SAndrew Jeffery             [weak{weak_from_this()},
237275f7c39SAndrew Jeffery              path{objpath.str}](sdbusplus::message_t& msg) {
238275f7c39SAndrew Jeffery                 if (auto self = weak.lock())
239275f7c39SAndrew Jeffery                 {
240275f7c39SAndrew Jeffery                     self->onMctpEndpointChange(msg);
241275f7c39SAndrew Jeffery                 }
242275f7c39SAndrew Jeffery                 else
243275f7c39SAndrew Jeffery                 {
244275f7c39SAndrew Jeffery                     info(
245275f7c39SAndrew Jeffery                         "The endpoint for the device at inventory path '{INVENTORY_PATH}' was destroyed concurrent to the removal of its state change match",
246275f7c39SAndrew Jeffery                         "INVENTORY_PATH", path);
247275f7c39SAndrew Jeffery                 }
248275f7c39SAndrew Jeffery             });
249275f7c39SAndrew Jeffery         connection->async_method_call(
250275f7c39SAndrew Jeffery             [weak{weak_from_this()},
251275f7c39SAndrew Jeffery              path{objpath.str}](const boost::system::error_code& ec,
252275f7c39SAndrew Jeffery                                 const std::variant<std::string>& value) {
253275f7c39SAndrew Jeffery                 if (ec)
254275f7c39SAndrew Jeffery                 {
255275f7c39SAndrew Jeffery                     debug(
256275f7c39SAndrew Jeffery                         "Failed to get current connectivity state: {ERROR_MESSAGE}",
257275f7c39SAndrew Jeffery                         "ERROR_MESSAGE", ec.message(), "ERROR_CATEGORY",
258275f7c39SAndrew Jeffery                         ec.category().name(), "ERROR_CODE", ec.value());
259275f7c39SAndrew Jeffery                     return;
260275f7c39SAndrew Jeffery                 }
261275f7c39SAndrew Jeffery 
262275f7c39SAndrew Jeffery                 if (auto self = weak.lock())
263275f7c39SAndrew Jeffery                 {
264275f7c39SAndrew Jeffery                     const std::string& connectivity =
265275f7c39SAndrew Jeffery                         std::get<std::string>(value);
266275f7c39SAndrew Jeffery                     self->updateEndpointConnectivity(connectivity);
267275f7c39SAndrew Jeffery                 }
268275f7c39SAndrew Jeffery                 else
269275f7c39SAndrew Jeffery                 {
270275f7c39SAndrew Jeffery                     info(
271275f7c39SAndrew Jeffery                         "The endpoint for the device at inventory path '{INVENTORY_PATH}' was destroyed concurrent to the completion of its connectivity state query",
272275f7c39SAndrew Jeffery                         "INVENTORY_PATH", path);
273275f7c39SAndrew Jeffery                 }
274275f7c39SAndrew Jeffery             },
275275f7c39SAndrew Jeffery             mctpdBusName, objpath.str, "org.freedesktop.DBus.Properties", "Get",
276275f7c39SAndrew Jeffery             mctpdEndpointControlInterface, "Connectivity");
277275f7c39SAndrew Jeffery     }
278275f7c39SAndrew Jeffery     catch (const sdbusplus::exception::SdBusError& err)
279275f7c39SAndrew Jeffery     {
280275f7c39SAndrew Jeffery         this->notifyDegraded = nullptr;
281275f7c39SAndrew Jeffery         this->notifyAvailable = nullptr;
282275f7c39SAndrew Jeffery         this->notifyRemoved = nullptr;
283275f7c39SAndrew Jeffery         std::throw_with_nested(
284275f7c39SAndrew Jeffery             MCTPException("Failed to register connectivity signal match"));
285275f7c39SAndrew Jeffery     }
286275f7c39SAndrew Jeffery }
remove()287275f7c39SAndrew Jeffery 
288275f7c39SAndrew Jeffery void MCTPDEndpoint::remove()
289275f7c39SAndrew Jeffery {
290275f7c39SAndrew Jeffery     connection->async_method_call(
291275f7c39SAndrew Jeffery         [self{shared_from_this()}](const boost::system::error_code& ec) {
292275f7c39SAndrew Jeffery             if (ec)
293275f7c39SAndrew Jeffery             {
294275f7c39SAndrew Jeffery                 debug("Failed to remove endpoint @ [ {MCTP_ENDPOINT} ]",
295275f7c39SAndrew Jeffery                       "MCTP_ENDPOINT", self->describe());
296275f7c39SAndrew Jeffery                 return;
297275f7c39SAndrew Jeffery             }
298275f7c39SAndrew Jeffery         },
299275f7c39SAndrew Jeffery         mctpdBusName, objpath.str, mctpdEndpointControlInterface, "Remove");
300275f7c39SAndrew Jeffery }
removed()301275f7c39SAndrew Jeffery 
302275f7c39SAndrew Jeffery void MCTPDEndpoint::removed()
303275f7c39SAndrew Jeffery {
304275f7c39SAndrew Jeffery     if (notifyRemoved)
305275f7c39SAndrew Jeffery     {
306275f7c39SAndrew Jeffery         notifyRemoved(shared_from_this());
307275f7c39SAndrew Jeffery     }
308275f7c39SAndrew Jeffery }
describe() const309275f7c39SAndrew Jeffery 
310275f7c39SAndrew Jeffery std::string MCTPDEndpoint::describe() const
311275f7c39SAndrew Jeffery {
312275f7c39SAndrew Jeffery     return std::format("network: {}, EID: {} | {}", mctp.network, mctp.eid,
313275f7c39SAndrew Jeffery                        dev->describe());
314275f7c39SAndrew Jeffery }
device() const315275f7c39SAndrew Jeffery 
316275f7c39SAndrew Jeffery std::shared_ptr<MCTPDevice> MCTPDEndpoint::device() const
317275f7c39SAndrew Jeffery {
318275f7c39SAndrew Jeffery     return dev;
319275f7c39SAndrew Jeffery }
320275f7c39SAndrew Jeffery 
match(const SensorData & config)321275f7c39SAndrew Jeffery std::optional<SensorBaseConfigMap>
322275f7c39SAndrew Jeffery     I2CMCTPDDevice::match(const SensorData& config)
323275f7c39SAndrew Jeffery {
324275f7c39SAndrew Jeffery     auto iface = config.find(configInterfaceName(configType));
325275f7c39SAndrew Jeffery     if (iface == config.end())
326275f7c39SAndrew Jeffery     {
327275f7c39SAndrew Jeffery         return std::nullopt;
328275f7c39SAndrew Jeffery     }
329275f7c39SAndrew Jeffery     return iface->second;
330275f7c39SAndrew Jeffery }
match(const std::set<std::string> & interfaces)331275f7c39SAndrew Jeffery 
332275f7c39SAndrew Jeffery bool I2CMCTPDDevice::match(const std::set<std::string>& interfaces)
333275f7c39SAndrew Jeffery {
334275f7c39SAndrew Jeffery     return interfaces.contains(configInterfaceName(configType));
335275f7c39SAndrew Jeffery }
from(const std::shared_ptr<sdbusplus::asio::connection> & connection,const SensorBaseConfigMap & iface)336275f7c39SAndrew Jeffery 
337275f7c39SAndrew Jeffery std::shared_ptr<I2CMCTPDDevice> I2CMCTPDDevice::from(
338275f7c39SAndrew Jeffery     const std::shared_ptr<sdbusplus::asio::connection>& connection,
339275f7c39SAndrew Jeffery     const SensorBaseConfigMap& iface)
340275f7c39SAndrew Jeffery {
341275f7c39SAndrew Jeffery     auto mType = iface.find("Type");
342275f7c39SAndrew Jeffery     if (mType == iface.end())
343275f7c39SAndrew Jeffery     {
344275f7c39SAndrew Jeffery         throw std::invalid_argument(
345275f7c39SAndrew Jeffery             "No 'Type' member found for provided configuration object");
346275f7c39SAndrew Jeffery     }
347275f7c39SAndrew Jeffery 
348275f7c39SAndrew Jeffery     auto type = std::visit(VariantToStringVisitor(), mType->second);
349275f7c39SAndrew Jeffery     if (type != configType)
350275f7c39SAndrew Jeffery     {
351275f7c39SAndrew Jeffery         throw std::invalid_argument("Not an SMBus device");
352275f7c39SAndrew Jeffery     }
353275f7c39SAndrew Jeffery 
354275f7c39SAndrew Jeffery     auto mAddress = iface.find("Address");
355275f7c39SAndrew Jeffery     auto mBus = iface.find("Bus");
356275f7c39SAndrew Jeffery     auto mName = iface.find("Name");
357275f7c39SAndrew Jeffery     if (mAddress == iface.end() || mBus == iface.end() || mName == iface.end())
358275f7c39SAndrew Jeffery     {
359275f7c39SAndrew Jeffery         throw std::invalid_argument(
360275f7c39SAndrew Jeffery             "Configuration object violates MCTPI2CTarget schema");
361275f7c39SAndrew Jeffery     }
362275f7c39SAndrew Jeffery 
363275f7c39SAndrew Jeffery     auto sAddress = std::visit(VariantToStringVisitor(), mAddress->second);
364275f7c39SAndrew Jeffery     std::uint8_t address{};
365275f7c39SAndrew Jeffery     auto [aptr, aec] = std::from_chars(
366275f7c39SAndrew Jeffery         sAddress.data(), sAddress.data() + sAddress.size(), address);
367275f7c39SAndrew Jeffery     if (aec != std::errc{})
368275f7c39SAndrew Jeffery     {
369275f7c39SAndrew Jeffery         throw std::invalid_argument("Bad device address");
370275f7c39SAndrew Jeffery     }
371275f7c39SAndrew Jeffery 
372275f7c39SAndrew Jeffery     auto sBus = std::visit(VariantToStringVisitor(), mBus->second);
373275f7c39SAndrew Jeffery     int bus{};
374275f7c39SAndrew Jeffery     auto [bptr,
375275f7c39SAndrew Jeffery           bec] = std::from_chars(sBus.data(), sBus.data() + sBus.size(), bus);
376275f7c39SAndrew Jeffery     if (bec != std::errc{})
377275f7c39SAndrew Jeffery     {
378275f7c39SAndrew Jeffery         throw std::invalid_argument("Bad bus index");
379275f7c39SAndrew Jeffery     }
380275f7c39SAndrew Jeffery 
381275f7c39SAndrew Jeffery     try
382275f7c39SAndrew Jeffery     {
383275f7c39SAndrew Jeffery         return std::make_shared<I2CMCTPDDevice>(connection, bus, address);
384275f7c39SAndrew Jeffery     }
385275f7c39SAndrew Jeffery     catch (const MCTPException& ex)
386275f7c39SAndrew Jeffery     {
387275f7c39SAndrew Jeffery         warning(
388275f7c39SAndrew Jeffery             "Failed to create I2CMCTPDDevice at [ bus: {I2C_BUS}, address: {I2C_ADDRESS} ]: {EXCEPTION}",
389275f7c39SAndrew Jeffery             "I2C_BUS", bus, "I2C_ADDRESS", address, "EXCEPTION", ex);
390275f7c39SAndrew Jeffery         return {};
391275f7c39SAndrew Jeffery     }
392275f7c39SAndrew Jeffery }
interfaceFromBus(int bus)393275f7c39SAndrew Jeffery 
394275f7c39SAndrew Jeffery std::string I2CMCTPDDevice::interfaceFromBus(int bus)
395275f7c39SAndrew Jeffery {
396275f7c39SAndrew Jeffery     std::filesystem::path netdir =
397275f7c39SAndrew Jeffery         std::format("/sys/bus/i2c/devices/i2c-{}/net", bus);
398275f7c39SAndrew Jeffery     std::error_code ec;
399275f7c39SAndrew Jeffery     std::filesystem::directory_iterator it(netdir, ec);
400275f7c39SAndrew Jeffery     if (ec || it == std::filesystem::end(it))
401275f7c39SAndrew Jeffery     {
402275f7c39SAndrew Jeffery         error("No net device associated with I2C bus {I2C_BUS} at {NET_DEVICE}",
403275f7c39SAndrew Jeffery               "I2C_BUS", bus, "NET_DEVICE", netdir);
404275f7c39SAndrew Jeffery         throw MCTPException("Bus is not configured as an MCTP interface");
405275f7c39SAndrew Jeffery     }
406275f7c39SAndrew Jeffery 
407275f7c39SAndrew Jeffery     return it->path().filename();
408275f7c39SAndrew Jeffery }
409