xref: /openbmc/dbus-sensors/src/DeviceMgmt.cpp (revision 6198435dc2bc6e07b46e32465876815d4ea86437)
1 #include "DeviceMgmt.hpp"
2 
3 #include "Utils.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 #include <cstdint>
8 #include <filesystem>
9 #include <fstream>
10 #include <iomanip>
11 #include <ios>
12 #include <optional>
13 #include <sstream>
14 #include <stdexcept>
15 #include <string>
16 #include <system_error>
17 #include <variant>
18 
getI2CDeviceParams(const I2CDeviceTypeMap & dtmap,const SensorBaseConfigMap & cfg)19 std::optional<I2CDeviceParams> getI2CDeviceParams(
20     const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg)
21 {
22     auto findType = cfg.find("Type");
23     auto findBus = cfg.find("Bus");
24     auto findAddr = cfg.find("Address");
25 
26     if (findType == cfg.end() || findBus == cfg.end() || findAddr == cfg.end())
27     {
28         return std::nullopt;
29     }
30 
31     const std::string* type = std::get_if<std::string>(&findType->second);
32     const uint64_t* bus = std::get_if<uint64_t>(&findBus->second);
33     const uint64_t* addr = std::get_if<uint64_t>(&findAddr->second);
34 
35     if (type == nullptr || bus == nullptr || addr == nullptr)
36     {
37         return std::nullopt;
38     }
39 
40     auto findDevType = dtmap.find(type->c_str());
41     if (findDevType == dtmap.end())
42     {
43         return std::nullopt;
44     }
45 
46     return I2CDeviceParams(findDevType->second, *bus, *addr);
47 }
48 
i2cBusPath(uint64_t bus)49 static std::filesystem::path i2cBusPath(uint64_t bus)
50 {
51     return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
52 }
53 
deviceDirName(uint64_t bus,uint64_t address)54 static std::string deviceDirName(uint64_t bus, uint64_t address)
55 {
56     std::ostringstream name;
57     name << bus << "-" << std::hex << std::setw(4) << std::setfill('0')
58          << address;
59     return name.str();
60 }
61 
devicePresent() const62 bool I2CDeviceParams::devicePresent() const
63 {
64     std::filesystem::path path = i2cBusPath(bus) / deviceDirName(bus, address);
65 
66     if (type->createsHWMon)
67     {
68         path /= "hwmon";
69     }
70 
71     // Ignore errors; anything but a clean 'true' is fine as 'false'
72     std::error_code ec;
73     return std::filesystem::exists(path, ec);
74 }
75 
deviceStatic() const76 bool I2CDeviceParams::deviceStatic() const
77 {
78     if (!devicePresent())
79     {
80         return false;
81     }
82 
83     std::filesystem::path ofNode =
84         i2cBusPath(bus) / deviceDirName(bus, address) / "of_node";
85 
86     // Ignore errors -- if of_node is present the device is a static DT node;
87     // otherwise we can assume we created it from userspace.
88     std::error_code ec;
89     return std::filesystem::exists(ofNode, ec);
90 }
91 
I2CDevice(I2CDeviceParams params)92 I2CDevice::I2CDevice(I2CDeviceParams params) : params(params)
93 {
94     if (create() != 0)
95     {
96         throw std::runtime_error("failed to instantiate i2c device");
97     }
98 }
99 
~I2CDevice()100 I2CDevice::~I2CDevice()
101 {
102     destroy();
103 }
104 
create() const105 int I2CDevice::create() const
106 {
107     // If it's already instantiated, there's nothing we need to do.
108     if (params.devicePresent())
109     {
110         return 0;
111     }
112 
113     // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
114     std::filesystem::path ctorPath = i2cBusPath(params.bus) / "new_device";
115     std::ofstream ctor(ctorPath);
116     if (!ctor.good())
117     {
118         lg2::error("Failed to open '{PATH}'", "PATH", ctorPath);
119         return -1;
120     }
121 
122     ctor << params.type->name << " " << params.address << "\n";
123     ctor.flush();
124     if (!ctor.good())
125     {
126         lg2::error("Failed to write to '{PATH}'", "PATH", ctorPath);
127         return -1;
128     }
129 
130     // Check if that created the requisite sysfs directory
131     if (!params.devicePresent())
132     {
133         destroy();
134         return -1;
135     }
136 
137     return 0;
138 }
139 
destroy() const140 int I2CDevice::destroy() const
141 {
142     // No params.devicePresent() check on this like in create(), since it
143     // might be used to clean up after a device instantiation that was only
144     // partially successful (i.e. when params.devicePresent() would return
145     // false but there's still a dummy i2c client device to remove)
146 
147     std::filesystem::path dtorPath = i2cBusPath(params.bus) / "delete_device";
148     std::ofstream dtor(dtorPath);
149     if (!dtor.good())
150     {
151         lg2::error("Failed to open '{PATH}'", "PATH", dtorPath);
152         return -1;
153     }
154 
155     dtor << params.address << "\n";
156     dtor.flush();
157     if (!dtor.good())
158     {
159         lg2::error("Failed to write to '{PATH}'", "PATH", dtorPath);
160         return -1;
161     }
162 
163     return 0;
164 }
165