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
getI2CDeviceParams(const I2CDeviceTypeMap & dtmap,const SensorBaseConfigMap & cfg)20 std::optional<I2CDeviceParams> getI2CDeviceParams(
21 const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg)
22 {
23 auto findType = cfg.find("Type");
24 auto findBus = cfg.find("Bus");
25 auto findAddr = cfg.find("Address");
26
27 if (findType == cfg.end() || findBus == cfg.end() || findAddr == cfg.end())
28 {
29 return std::nullopt;
30 }
31
32 const std::string* type = std::get_if<std::string>(&findType->second);
33 const uint64_t* bus = std::get_if<uint64_t>(&findBus->second);
34 const uint64_t* addr = std::get_if<uint64_t>(&findAddr->second);
35
36 if (type == nullptr || bus == nullptr || addr == nullptr)
37 {
38 return std::nullopt;
39 }
40
41 auto findDevType = dtmap.find(type->c_str());
42 if (findDevType == dtmap.end())
43 {
44 return std::nullopt;
45 }
46
47 return I2CDeviceParams(findDevType->second, *bus, *addr);
48 }
49
i2cBusPath(uint64_t bus)50 static fs::path i2cBusPath(uint64_t bus)
51 {
52 return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
53 }
54
deviceDirName(uint64_t bus,uint64_t address)55 static std::string deviceDirName(uint64_t bus, uint64_t address)
56 {
57 std::ostringstream name;
58 name << bus << "-" << std::hex << std::setw(4) << std::setfill('0')
59 << address;
60 return name.str();
61 }
62
devicePresent() const63 bool I2CDeviceParams::devicePresent() const
64 {
65 fs::path path = i2cBusPath(bus) / deviceDirName(bus, address);
66
67 if (type->createsHWMon)
68 {
69 path /= "hwmon";
70 }
71
72 // Ignore errors; anything but a clean 'true' is fine as 'false'
73 std::error_code ec;
74 return fs::exists(path, ec);
75 }
76
deviceStatic() const77 bool I2CDeviceParams::deviceStatic() const
78 {
79 if (!devicePresent())
80 {
81 return false;
82 }
83
84 fs::path ofNode = 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 fs::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 fs::path ctorPath = i2cBusPath(params.bus) / "new_device";
115 std::ofstream ctor(ctorPath);
116 if (!ctor.good())
117 {
118 std::cerr << "Failed to open " << ctorPath << "\n";
119 return -1;
120 }
121
122 ctor << params.type->name << " " << params.address << "\n";
123 ctor.flush();
124 if (!ctor.good())
125 {
126 std::cerr << "Failed to write to " << ctorPath << "\n";
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 fs::path dtorPath = i2cBusPath(params.bus) / "delete_device";
148 std::ofstream dtor(dtorPath);
149 if (!dtor.good())
150 {
151 std::cerr << "Failed to open " << dtorPath << "\n";
152 return -1;
153 }
154
155 dtor << params.address << "\n";
156 dtor.flush();
157 if (!dtor.good())
158 {
159 std::cerr << "Failed to write to " << dtorPath << "\n";
160 return -1;
161 }
162
163 return 0;
164 }
165