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