1*275f7c39SAndrew Jeffery #include "MCTPEndpoint.hpp"
2*275f7c39SAndrew Jeffery 
3*275f7c39SAndrew Jeffery #include "Utils.hpp"
4*275f7c39SAndrew Jeffery #include "VariantVisitors.hpp"
5*275f7c39SAndrew Jeffery 
6*275f7c39SAndrew Jeffery #include <bits/fs_dir.h>
7*275f7c39SAndrew Jeffery 
8*275f7c39SAndrew Jeffery #include <boost/system/detail/errc.hpp>
9*275f7c39SAndrew Jeffery #include <phosphor-logging/lg2.hpp>
10*275f7c39SAndrew Jeffery #include <sdbusplus/asio/connection.hpp>
11*275f7c39SAndrew Jeffery #include <sdbusplus/bus.hpp>
12*275f7c39SAndrew Jeffery #include <sdbusplus/bus/match.hpp>
13*275f7c39SAndrew Jeffery #include <sdbusplus/exception.hpp>
14*275f7c39SAndrew Jeffery #include <sdbusplus/message.hpp>
15*275f7c39SAndrew Jeffery #include <sdbusplus/message/native_types.hpp>
16*275f7c39SAndrew Jeffery 
17*275f7c39SAndrew Jeffery #include <cassert>
18*275f7c39SAndrew Jeffery #include <charconv>
19*275f7c39SAndrew Jeffery #include <cstdint>
20*275f7c39SAndrew Jeffery #include <exception>
21*275f7c39SAndrew Jeffery #include <filesystem>
22*275f7c39SAndrew Jeffery #include <format>
23*275f7c39SAndrew Jeffery #include <functional>
24*275f7c39SAndrew Jeffery #include <map>
25*275f7c39SAndrew Jeffery #include <memory>
26*275f7c39SAndrew Jeffery #include <optional>
27*275f7c39SAndrew Jeffery #include <set>
28*275f7c39SAndrew Jeffery #include <stdexcept>
29*275f7c39SAndrew Jeffery #include <string>
30*275f7c39SAndrew Jeffery #include <system_error>
31*275f7c39SAndrew Jeffery #include <utility>
32*275f7c39SAndrew Jeffery #include <variant>
33*275f7c39SAndrew Jeffery #include <vector>
34*275f7c39SAndrew Jeffery 
35*275f7c39SAndrew Jeffery PHOSPHOR_LOG2_USING;
36*275f7c39SAndrew Jeffery 
37*275f7c39SAndrew Jeffery static constexpr const char* mctpdBusName = "xyz.openbmc_project.MCTP";
38*275f7c39SAndrew Jeffery static constexpr const char* mctpdControlPath = "/xyz/openbmc_project/mctp";
39*275f7c39SAndrew Jeffery static constexpr const char* mctpdControlInterface =
40*275f7c39SAndrew Jeffery     "au.com.CodeConstruct.MCTP";
41*275f7c39SAndrew Jeffery static constexpr const char* mctpdEndpointControlInterface =
42*275f7c39SAndrew Jeffery     "au.com.CodeConstruct.MCTP.Endpoint";
43*275f7c39SAndrew Jeffery 
MCTPDDevice(const std::shared_ptr<sdbusplus::asio::connection> & connection,const std::string & interface,const std::vector<uint8_t> & physaddr)44*275f7c39SAndrew Jeffery MCTPDDevice::MCTPDDevice(
45*275f7c39SAndrew Jeffery     const std::shared_ptr<sdbusplus::asio::connection>& connection,
46*275f7c39SAndrew Jeffery     const std::string& interface, const std::vector<uint8_t>& physaddr) :
47*275f7c39SAndrew Jeffery     connection(connection), interface(interface), physaddr(physaddr)
48*275f7c39SAndrew Jeffery {}
49*275f7c39SAndrew Jeffery 
onEndpointInterfacesRemoved(const std::weak_ptr<MCTPDDevice> & weak,const std::string & objpath,sdbusplus::message_t & msg)50*275f7c39SAndrew Jeffery void MCTPDDevice::onEndpointInterfacesRemoved(
51*275f7c39SAndrew Jeffery     const std::weak_ptr<MCTPDDevice>& weak, const std::string& objpath,
52*275f7c39SAndrew Jeffery     sdbusplus::message_t& msg)
53*275f7c39SAndrew Jeffery {
54*275f7c39SAndrew Jeffery     auto path = msg.unpack<sdbusplus::message::object_path>();
55*275f7c39SAndrew Jeffery     assert(path.str == objpath);
56*275f7c39SAndrew Jeffery 
57*275f7c39SAndrew Jeffery     auto removedIfaces = msg.unpack<std::set<std::string>>();
58*275f7c39SAndrew Jeffery     if (!removedIfaces.contains(mctpdEndpointControlInterface))
59*275f7c39SAndrew Jeffery     {
60*275f7c39SAndrew Jeffery         return;
61*275f7c39SAndrew Jeffery     }
62*275f7c39SAndrew Jeffery 
63*275f7c39SAndrew Jeffery     if (auto self = weak.lock())
64*275f7c39SAndrew Jeffery     {
65*275f7c39SAndrew Jeffery         self->endpointRemoved();
66*275f7c39SAndrew Jeffery     }
67*275f7c39SAndrew Jeffery     else
68*275f7c39SAndrew Jeffery     {
69*275f7c39SAndrew Jeffery         info(
70*275f7c39SAndrew Jeffery             "Device for inventory at '{INVENTORY_PATH}' was destroyed concurrent to endpoint removal",
71*275f7c39SAndrew Jeffery             "INVENTORY_PATH", objpath);
72*275f7c39SAndrew Jeffery     }
73*275f7c39SAndrew Jeffery }
74*275f7c39SAndrew 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)75*275f7c39SAndrew Jeffery void MCTPDDevice::finaliseEndpoint(
76*275f7c39SAndrew Jeffery     const std::string& objpath, uint8_t eid, int network,
77*275f7c39SAndrew Jeffery     std::function<void(const std::error_code& ec,
78*275f7c39SAndrew Jeffery                        const std::shared_ptr<MCTPEndpoint>& ep)>& added)
79*275f7c39SAndrew Jeffery {
80*275f7c39SAndrew Jeffery     const auto matchSpec =
81*275f7c39SAndrew Jeffery         sdbusplus::bus::match::rules::interfacesRemovedAtPath(objpath);
82*275f7c39SAndrew Jeffery     removeMatch = std::make_unique<sdbusplus::bus::match_t>(
83*275f7c39SAndrew Jeffery         *connection, matchSpec,
84*275f7c39SAndrew Jeffery         std::bind_front(MCTPDDevice::onEndpointInterfacesRemoved,
85*275f7c39SAndrew Jeffery                         weak_from_this(), objpath));
86*275f7c39SAndrew Jeffery     endpoint = std::make_shared<MCTPDEndpoint>(shared_from_this(), connection,
87*275f7c39SAndrew Jeffery                                                objpath, network, eid);
88*275f7c39SAndrew Jeffery     added({}, endpoint);
89*275f7c39SAndrew Jeffery }
90*275f7c39SAndrew Jeffery 
setup(std::function<void (const std::error_code & ec,const std::shared_ptr<MCTPEndpoint> & ep)> && added)91*275f7c39SAndrew Jeffery void MCTPDDevice::setup(
92*275f7c39SAndrew Jeffery     std::function<void(const std::error_code& ec,
93*275f7c39SAndrew Jeffery                        const std::shared_ptr<MCTPEndpoint>& ep)>&& added)
94*275f7c39SAndrew Jeffery {
95*275f7c39SAndrew Jeffery     // Use a lambda to separate state validation from business logic,
96*275f7c39SAndrew Jeffery     // where the business logic for a successful setup() is encoded in
97*275f7c39SAndrew Jeffery     // MctpdDevice::finaliseEndpoint()
98*275f7c39SAndrew Jeffery     auto onSetup = [weak{weak_from_this()}, added{std::move(added)}](
99*275f7c39SAndrew Jeffery                        const boost::system::error_code& ec, uint8_t eid,
100*275f7c39SAndrew Jeffery                        int network, const std::string& objpath,
101*275f7c39SAndrew Jeffery                        bool allocated [[maybe_unused]]) mutable {
102*275f7c39SAndrew Jeffery         if (ec)
103*275f7c39SAndrew Jeffery         {
104*275f7c39SAndrew Jeffery             added(ec, {});
105*275f7c39SAndrew Jeffery             return;
106*275f7c39SAndrew Jeffery         }
107*275f7c39SAndrew Jeffery 
108*275f7c39SAndrew Jeffery         if (auto self = weak.lock())
109*275f7c39SAndrew Jeffery         {
110*275f7c39SAndrew Jeffery             self->finaliseEndpoint(objpath, eid, network, added);
111*275f7c39SAndrew Jeffery         }
112*275f7c39SAndrew Jeffery         else
113*275f7c39SAndrew Jeffery         {
114*275f7c39SAndrew Jeffery             info(
115*275f7c39SAndrew Jeffery                 "Device object for inventory at '{INVENTORY_PATH}' was destroyed concurrent to completion of its endpoint setup",
116*275f7c39SAndrew Jeffery                 "INVENTORY_PATH", objpath);
117*275f7c39SAndrew Jeffery         }
118*275f7c39SAndrew Jeffery     };
119*275f7c39SAndrew Jeffery     connection->async_method_call(onSetup, mctpdBusName, mctpdControlPath,
120*275f7c39SAndrew Jeffery                                   mctpdControlInterface, "AssignEndpoint",
121*275f7c39SAndrew Jeffery                                   interface, physaddr);
122*275f7c39SAndrew Jeffery }
123*275f7c39SAndrew Jeffery 
endpointRemoved()124*275f7c39SAndrew Jeffery void MCTPDDevice::endpointRemoved()
125*275f7c39SAndrew Jeffery {
126*275f7c39SAndrew Jeffery     if (endpoint)
127*275f7c39SAndrew Jeffery     {
128*275f7c39SAndrew Jeffery         debug("Endpoint removed @ [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT",
129*275f7c39SAndrew Jeffery               endpoint->describe());
130*275f7c39SAndrew Jeffery         removeMatch.reset();
131*275f7c39SAndrew Jeffery         endpoint->removed();
132*275f7c39SAndrew Jeffery         endpoint.reset();
133*275f7c39SAndrew Jeffery     }
134*275f7c39SAndrew Jeffery }
135*275f7c39SAndrew Jeffery 
remove()136*275f7c39SAndrew Jeffery void MCTPDDevice::remove()
137*275f7c39SAndrew Jeffery {
138*275f7c39SAndrew Jeffery     if (endpoint)
139*275f7c39SAndrew Jeffery     {
140*275f7c39SAndrew Jeffery         debug("Removing endpoint @ [ {MCTP_ENDPOINT} ]", "MCTP_ENDPOINT",
141*275f7c39SAndrew Jeffery               endpoint->describe());
142*275f7c39SAndrew Jeffery         endpoint->remove();
143*275f7c39SAndrew Jeffery     }
144*275f7c39SAndrew Jeffery }
145*275f7c39SAndrew Jeffery 
describe() const146*275f7c39SAndrew Jeffery std::string MCTPDDevice::describe() const
147*275f7c39SAndrew Jeffery {
148*275f7c39SAndrew Jeffery     std::string description = std::format("interface: {}", interface);
149*275f7c39SAndrew Jeffery     if (!physaddr.empty())
150*275f7c39SAndrew Jeffery     {
151*275f7c39SAndrew Jeffery         description.append(", address: 0x [ ");
152*275f7c39SAndrew Jeffery         auto it = physaddr.begin();
153*275f7c39SAndrew Jeffery         for (; it != physaddr.end() - 1; it++)
154*275f7c39SAndrew Jeffery         {
155*275f7c39SAndrew Jeffery             description.append(std::format("{:02x} ", *it));
156*275f7c39SAndrew Jeffery         }
157*275f7c39SAndrew Jeffery         description.append(std::format("{:02x} ]", *it));
158*275f7c39SAndrew Jeffery     }
159*275f7c39SAndrew Jeffery     return description;
160*275f7c39SAndrew Jeffery }
161*275f7c39SAndrew Jeffery 
path(const std::shared_ptr<MCTPEndpoint> & ep)162*275f7c39SAndrew Jeffery std::string MCTPDEndpoint::path(const std::shared_ptr<MCTPEndpoint>& ep)
163*275f7c39SAndrew Jeffery {
164*275f7c39SAndrew Jeffery     return std::format("/xyz/openbmc_project/mctp/{}/{}", ep->network(),
165*275f7c39SAndrew Jeffery                        ep->eid());
166*275f7c39SAndrew Jeffery }
167*275f7c39SAndrew Jeffery 
onMctpEndpointChange(sdbusplus::message_t & msg)168*275f7c39SAndrew Jeffery void MCTPDEndpoint::onMctpEndpointChange(sdbusplus::message_t& msg)
169*275f7c39SAndrew Jeffery {
170*275f7c39SAndrew Jeffery     auto [iface, changed, _] =
171*275f7c39SAndrew Jeffery         msg.unpack<std::string, std::map<std::string, BasicVariantType>,
172*275f7c39SAndrew Jeffery                    std::vector<std::string>>();
173*275f7c39SAndrew Jeffery     if (iface != mctpdEndpointControlInterface)
174*275f7c39SAndrew Jeffery     {
175*275f7c39SAndrew Jeffery         return;
176*275f7c39SAndrew Jeffery     }
177*275f7c39SAndrew Jeffery 
178*275f7c39SAndrew Jeffery     auto it = changed.find("Connectivity");
179*275f7c39SAndrew Jeffery     if (it == changed.end())
180*275f7c39SAndrew Jeffery     {
181*275f7c39SAndrew Jeffery         return;
182*275f7c39SAndrew Jeffery     }
183*275f7c39SAndrew Jeffery 
184*275f7c39SAndrew Jeffery     updateEndpointConnectivity(std::get<std::string>(it->second));
185*275f7c39SAndrew Jeffery }
186*275f7c39SAndrew Jeffery 
updateEndpointConnectivity(const std::string & connectivity)187*275f7c39SAndrew Jeffery void MCTPDEndpoint::updateEndpointConnectivity(const std::string& connectivity)
188*275f7c39SAndrew Jeffery {
189*275f7c39SAndrew Jeffery     if (connectivity == "Degraded")
190*275f7c39SAndrew Jeffery     {
191*275f7c39SAndrew Jeffery         if (notifyDegraded)
192*275f7c39SAndrew Jeffery         {
193*275f7c39SAndrew Jeffery             notifyDegraded(shared_from_this());
194*275f7c39SAndrew Jeffery         }
195*275f7c39SAndrew Jeffery     }
196*275f7c39SAndrew Jeffery     else if (connectivity == "Available")
197*275f7c39SAndrew Jeffery     {
198*275f7c39SAndrew Jeffery         if (notifyAvailable)
199*275f7c39SAndrew Jeffery         {
200*275f7c39SAndrew Jeffery             notifyAvailable(shared_from_this());
201*275f7c39SAndrew Jeffery         }
202*275f7c39SAndrew Jeffery     }
203*275f7c39SAndrew Jeffery     else
204*275f7c39SAndrew Jeffery     {
205*275f7c39SAndrew Jeffery         debug("Unrecognised connectivity state: '{CONNECTIVITY_STATE}'",
206*275f7c39SAndrew Jeffery               "CONNECTIVITY_STATE", connectivity);
207*275f7c39SAndrew Jeffery     }
208*275f7c39SAndrew Jeffery }
209*275f7c39SAndrew Jeffery 
network() const210*275f7c39SAndrew Jeffery int MCTPDEndpoint::network() const
211*275f7c39SAndrew Jeffery {
212*275f7c39SAndrew Jeffery     return mctp.network;
213*275f7c39SAndrew Jeffery }
214*275f7c39SAndrew Jeffery 
eid() const215*275f7c39SAndrew Jeffery uint8_t MCTPDEndpoint::eid() const
216*275f7c39SAndrew Jeffery {
217*275f7c39SAndrew Jeffery     return mctp.eid;
218*275f7c39SAndrew Jeffery }
219*275f7c39SAndrew Jeffery 
subscribe(Event && degraded,Event && available,Event && removed)220*275f7c39SAndrew Jeffery void MCTPDEndpoint::subscribe(Event&& degraded, Event&& available,
221*275f7c39SAndrew Jeffery                               Event&& removed)
222*275f7c39SAndrew Jeffery {
223*275f7c39SAndrew Jeffery     const auto matchSpec =
224*275f7c39SAndrew Jeffery         sdbusplus::bus::match::rules::propertiesChangedNamespace(
225*275f7c39SAndrew Jeffery             objpath.str, mctpdEndpointControlInterface);
226*275f7c39SAndrew Jeffery 
227*275f7c39SAndrew Jeffery     this->notifyDegraded = std::move(degraded);
228*275f7c39SAndrew Jeffery     this->notifyAvailable = std::move(available);
229*275f7c39SAndrew Jeffery     this->notifyRemoved = std::move(removed);
230*275f7c39SAndrew Jeffery 
231*275f7c39SAndrew Jeffery     try
232*275f7c39SAndrew Jeffery     {
233*275f7c39SAndrew Jeffery         connectivityMatch.emplace(
234*275f7c39SAndrew Jeffery             static_cast<sdbusplus::bus_t&>(*connection), matchSpec,
235*275f7c39SAndrew Jeffery             [weak{weak_from_this()},
236*275f7c39SAndrew Jeffery              path{objpath.str}](sdbusplus::message_t& msg) {
237*275f7c39SAndrew Jeffery                 if (auto self = weak.lock())
238*275f7c39SAndrew Jeffery                 {
239*275f7c39SAndrew Jeffery                     self->onMctpEndpointChange(msg);
240*275f7c39SAndrew Jeffery                 }
241*275f7c39SAndrew Jeffery                 else
242*275f7c39SAndrew Jeffery                 {
243*275f7c39SAndrew Jeffery                     info(
244*275f7c39SAndrew Jeffery                         "The endpoint for the device at inventory path '{INVENTORY_PATH}' was destroyed concurrent to the removal of its state change match",
245*275f7c39SAndrew Jeffery                         "INVENTORY_PATH", path);
246*275f7c39SAndrew Jeffery                 }
247*275f7c39SAndrew Jeffery             });
248*275f7c39SAndrew Jeffery         connection->async_method_call(
249*275f7c39SAndrew Jeffery             [weak{weak_from_this()},
250*275f7c39SAndrew Jeffery              path{objpath.str}](const boost::system::error_code& ec,
251*275f7c39SAndrew Jeffery                                 const std::variant<std::string>& value) {
252*275f7c39SAndrew Jeffery                 if (ec)
253*275f7c39SAndrew Jeffery                 {
254*275f7c39SAndrew Jeffery                     debug(
255*275f7c39SAndrew Jeffery                         "Failed to get current connectivity state: {ERROR_MESSAGE}",
256*275f7c39SAndrew Jeffery                         "ERROR_MESSAGE", ec.message(), "ERROR_CATEGORY",
257*275f7c39SAndrew Jeffery                         ec.category().name(), "ERROR_CODE", ec.value());
258*275f7c39SAndrew Jeffery                     return;
259*275f7c39SAndrew Jeffery                 }
260*275f7c39SAndrew Jeffery 
261*275f7c39SAndrew Jeffery                 if (auto self = weak.lock())
262*275f7c39SAndrew Jeffery                 {
263*275f7c39SAndrew Jeffery                     const std::string& connectivity =
264*275f7c39SAndrew Jeffery                         std::get<std::string>(value);
265*275f7c39SAndrew Jeffery                     self->updateEndpointConnectivity(connectivity);
266*275f7c39SAndrew Jeffery                 }
267*275f7c39SAndrew Jeffery                 else
268*275f7c39SAndrew Jeffery                 {
269*275f7c39SAndrew Jeffery                     info(
270*275f7c39SAndrew Jeffery                         "The endpoint for the device at inventory path '{INVENTORY_PATH}' was destroyed concurrent to the completion of its connectivity state query",
271*275f7c39SAndrew Jeffery                         "INVENTORY_PATH", path);
272*275f7c39SAndrew Jeffery                 }
273*275f7c39SAndrew Jeffery             },
274*275f7c39SAndrew Jeffery             mctpdBusName, objpath.str, "org.freedesktop.DBus.Properties", "Get",
275*275f7c39SAndrew Jeffery             mctpdEndpointControlInterface, "Connectivity");
276*275f7c39SAndrew Jeffery     }
277*275f7c39SAndrew Jeffery     catch (const sdbusplus::exception::SdBusError& err)
278*275f7c39SAndrew Jeffery     {
279*275f7c39SAndrew Jeffery         this->notifyDegraded = nullptr;
280*275f7c39SAndrew Jeffery         this->notifyAvailable = nullptr;
281*275f7c39SAndrew Jeffery         this->notifyRemoved = nullptr;
282*275f7c39SAndrew Jeffery         std::throw_with_nested(
283*275f7c39SAndrew Jeffery             MCTPException("Failed to register connectivity signal match"));
284*275f7c39SAndrew Jeffery     }
285*275f7c39SAndrew Jeffery }
286*275f7c39SAndrew Jeffery 
remove()287*275f7c39SAndrew Jeffery void MCTPDEndpoint::remove()
288*275f7c39SAndrew Jeffery {
289*275f7c39SAndrew Jeffery     connection->async_method_call(
290*275f7c39SAndrew Jeffery         [self{shared_from_this()}](const boost::system::error_code& ec) {
291*275f7c39SAndrew Jeffery             if (ec)
292*275f7c39SAndrew Jeffery             {
293*275f7c39SAndrew Jeffery                 debug("Failed to remove endpoint @ [ {MCTP_ENDPOINT} ]",
294*275f7c39SAndrew Jeffery                       "MCTP_ENDPOINT", self->describe());
295*275f7c39SAndrew Jeffery                 return;
296*275f7c39SAndrew Jeffery             }
297*275f7c39SAndrew Jeffery         },
298*275f7c39SAndrew Jeffery         mctpdBusName, objpath.str, mctpdEndpointControlInterface, "Remove");
299*275f7c39SAndrew Jeffery }
300*275f7c39SAndrew Jeffery 
removed()301*275f7c39SAndrew Jeffery void MCTPDEndpoint::removed()
302*275f7c39SAndrew Jeffery {
303*275f7c39SAndrew Jeffery     if (notifyRemoved)
304*275f7c39SAndrew Jeffery     {
305*275f7c39SAndrew Jeffery         notifyRemoved(shared_from_this());
306*275f7c39SAndrew Jeffery     }
307*275f7c39SAndrew Jeffery }
308*275f7c39SAndrew Jeffery 
describe() const309*275f7c39SAndrew Jeffery std::string MCTPDEndpoint::describe() const
310*275f7c39SAndrew Jeffery {
311*275f7c39SAndrew Jeffery     return std::format("network: {}, EID: {} | {}", mctp.network, mctp.eid,
312*275f7c39SAndrew Jeffery                        dev->describe());
313*275f7c39SAndrew Jeffery }
314*275f7c39SAndrew Jeffery 
device() const315*275f7c39SAndrew Jeffery std::shared_ptr<MCTPDevice> MCTPDEndpoint::device() const
316*275f7c39SAndrew Jeffery {
317*275f7c39SAndrew Jeffery     return dev;
318*275f7c39SAndrew Jeffery }
319*275f7c39SAndrew Jeffery 
320*275f7c39SAndrew Jeffery std::optional<SensorBaseConfigMap>
match(const SensorData & config)321*275f7c39SAndrew Jeffery     I2CMCTPDDevice::match(const SensorData& config)
322*275f7c39SAndrew Jeffery {
323*275f7c39SAndrew Jeffery     auto iface = config.find(configInterfaceName(configType));
324*275f7c39SAndrew Jeffery     if (iface == config.end())
325*275f7c39SAndrew Jeffery     {
326*275f7c39SAndrew Jeffery         return std::nullopt;
327*275f7c39SAndrew Jeffery     }
328*275f7c39SAndrew Jeffery     return iface->second;
329*275f7c39SAndrew Jeffery }
330*275f7c39SAndrew Jeffery 
match(const std::set<std::string> & interfaces)331*275f7c39SAndrew Jeffery bool I2CMCTPDDevice::match(const std::set<std::string>& interfaces)
332*275f7c39SAndrew Jeffery {
333*275f7c39SAndrew Jeffery     return interfaces.contains(configInterfaceName(configType));
334*275f7c39SAndrew Jeffery }
335*275f7c39SAndrew Jeffery 
from(const std::shared_ptr<sdbusplus::asio::connection> & connection,const SensorBaseConfigMap & iface)336*275f7c39SAndrew Jeffery std::shared_ptr<I2CMCTPDDevice> I2CMCTPDDevice::from(
337*275f7c39SAndrew Jeffery     const std::shared_ptr<sdbusplus::asio::connection>& connection,
338*275f7c39SAndrew Jeffery     const SensorBaseConfigMap& iface)
339*275f7c39SAndrew Jeffery {
340*275f7c39SAndrew Jeffery     auto mType = iface.find("Type");
341*275f7c39SAndrew Jeffery     if (mType == iface.end())
342*275f7c39SAndrew Jeffery     {
343*275f7c39SAndrew Jeffery         throw std::invalid_argument(
344*275f7c39SAndrew Jeffery             "No 'Type' member found for provided configuration object");
345*275f7c39SAndrew Jeffery     }
346*275f7c39SAndrew Jeffery 
347*275f7c39SAndrew Jeffery     auto type = std::visit(VariantToStringVisitor(), mType->second);
348*275f7c39SAndrew Jeffery     if (type != configType)
349*275f7c39SAndrew Jeffery     {
350*275f7c39SAndrew Jeffery         throw std::invalid_argument("Not an SMBus device");
351*275f7c39SAndrew Jeffery     }
352*275f7c39SAndrew Jeffery 
353*275f7c39SAndrew Jeffery     auto mAddress = iface.find("Address");
354*275f7c39SAndrew Jeffery     auto mBus = iface.find("Bus");
355*275f7c39SAndrew Jeffery     auto mName = iface.find("Name");
356*275f7c39SAndrew Jeffery     if (mAddress == iface.end() || mBus == iface.end() || mName == iface.end())
357*275f7c39SAndrew Jeffery     {
358*275f7c39SAndrew Jeffery         throw std::invalid_argument(
359*275f7c39SAndrew Jeffery             "Configuration object violates MCTPI2CTarget schema");
360*275f7c39SAndrew Jeffery     }
361*275f7c39SAndrew Jeffery 
362*275f7c39SAndrew Jeffery     auto sAddress = std::visit(VariantToStringVisitor(), mAddress->second);
363*275f7c39SAndrew Jeffery     std::uint8_t address{};
364*275f7c39SAndrew Jeffery     auto [aptr, aec] = std::from_chars(
365*275f7c39SAndrew Jeffery         sAddress.data(), sAddress.data() + sAddress.size(), address);
366*275f7c39SAndrew Jeffery     if (aec != std::errc{})
367*275f7c39SAndrew Jeffery     {
368*275f7c39SAndrew Jeffery         throw std::invalid_argument("Bad device address");
369*275f7c39SAndrew Jeffery     }
370*275f7c39SAndrew Jeffery 
371*275f7c39SAndrew Jeffery     auto sBus = std::visit(VariantToStringVisitor(), mBus->second);
372*275f7c39SAndrew Jeffery     int bus{};
373*275f7c39SAndrew Jeffery     auto [bptr,
374*275f7c39SAndrew Jeffery           bec] = std::from_chars(sBus.data(), sBus.data() + sBus.size(), bus);
375*275f7c39SAndrew Jeffery     if (bec != std::errc{})
376*275f7c39SAndrew Jeffery     {
377*275f7c39SAndrew Jeffery         throw std::invalid_argument("Bad bus index");
378*275f7c39SAndrew Jeffery     }
379*275f7c39SAndrew Jeffery 
380*275f7c39SAndrew Jeffery     try
381*275f7c39SAndrew Jeffery     {
382*275f7c39SAndrew Jeffery         return std::make_shared<I2CMCTPDDevice>(connection, bus, address);
383*275f7c39SAndrew Jeffery     }
384*275f7c39SAndrew Jeffery     catch (const MCTPException& ex)
385*275f7c39SAndrew Jeffery     {
386*275f7c39SAndrew Jeffery         warning(
387*275f7c39SAndrew Jeffery             "Failed to create I2CMCTPDDevice at [ bus: {I2C_BUS}, address: {I2C_ADDRESS} ]: {EXCEPTION}",
388*275f7c39SAndrew Jeffery             "I2C_BUS", bus, "I2C_ADDRESS", address, "EXCEPTION", ex);
389*275f7c39SAndrew Jeffery         return {};
390*275f7c39SAndrew Jeffery     }
391*275f7c39SAndrew Jeffery }
392*275f7c39SAndrew Jeffery 
interfaceFromBus(int bus)393*275f7c39SAndrew Jeffery std::string I2CMCTPDDevice::interfaceFromBus(int bus)
394*275f7c39SAndrew Jeffery {
395*275f7c39SAndrew Jeffery     std::filesystem::path netdir =
396*275f7c39SAndrew Jeffery         std::format("/sys/bus/i2c/devices/i2c-{}/net", bus);
397*275f7c39SAndrew Jeffery     std::error_code ec;
398*275f7c39SAndrew Jeffery     std::filesystem::directory_iterator it(netdir, ec);
399*275f7c39SAndrew Jeffery     if (ec || it == std::filesystem::end(it))
400*275f7c39SAndrew Jeffery     {
401*275f7c39SAndrew Jeffery         error("No net device associated with I2C bus {I2C_BUS} at {NET_DEVICE}",
402*275f7c39SAndrew Jeffery               "I2C_BUS", bus, "NET_DEVICE", netdir);
403*275f7c39SAndrew Jeffery         throw MCTPException("Bus is not configured as an MCTP interface");
404*275f7c39SAndrew Jeffery     }
405*275f7c39SAndrew Jeffery 
406*275f7c39SAndrew Jeffery     return it->path().filename();
407*275f7c39SAndrew Jeffery }
408