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