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