blob: ffc037613e38700277b564a251419aa76e0f6b4d [file] [log] [blame]
Andrew Jefferye73bd0a2023-01-25 10:39:57 +10301#include "DeviceMgmt.hpp"
Zev Weissdabd48d2022-08-03 15:43:17 -07002
3#include <filesystem>
4#include <fstream>
5
6namespace fs = std::filesystem;
7
Zev Weiss3ee959a2022-09-21 17:16:28 -07008std::optional<I2CDeviceParams>
9 getI2CDeviceParams(const I2CDeviceTypeMap& dtmap,
10 const SensorBaseConfigMap& cfg)
Zev Weissdabd48d2022-08-03 15:43:17 -070011{
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
Zev Weiss3ee959a2022-09-21 17:16:28 -070036 return I2CDeviceParams(findDevType->second, *bus, *addr);
Zev Weissdabd48d2022-08-03 15:43:17 -070037}
38
39static fs::path i2cBusPath(uint64_t bus)
40{
41 return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
42}
43
44static 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
Zev Weiss3ee959a2022-09-21 17:16:28 -070052bool I2CDeviceParams::devicePresent(void) const
Zev Weissdabd48d2022-08-03 15:43:17 -070053{
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
Zev Weiss41f49c02022-09-21 17:27:18 -070066bool 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
Zev Weiss3ee959a2022-09-21 17:16:28 -070081I2CDevice::I2CDevice(I2CDeviceParams params) : params(params)
82{
83 if (create() != 0)
84 {
85 throw std::runtime_error("failed to instantiate i2c device");
86 }
87}
88
89I2CDevice::~I2CDevice()
90{
91 destroy();
92}
93
Zev Weissdabd48d2022-08-03 15:43:17 -070094int I2CDevice::create(void) const
95{
96 // If it's already instantiated, there's nothing we need to do.
Zev Weiss3ee959a2022-09-21 17:16:28 -070097 if (params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -070098 {
99 return 0;
100 }
101
102 // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
Zev Weiss3ee959a2022-09-21 17:16:28 -0700103 fs::path ctorPath = i2cBusPath(params.bus) / "new_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700104 std::ofstream ctor(ctorPath);
105 if (!ctor.good())
106 {
107 std::cerr << "Failed to open " << ctorPath << "\n";
108 return -1;
109 }
110
Zev Weiss3ee959a2022-09-21 17:16:28 -0700111 ctor << params.type->name << " " << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700112 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
Zev Weiss3ee959a2022-09-21 17:16:28 -0700120 if (!params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -0700121 {
122 destroy();
123 return -1;
124 }
125
126 return 0;
127}
128
129int I2CDevice::destroy(void) const
130{
Zev Weiss3ee959a2022-09-21 17:16:28 -0700131 // 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)
Zev Weissdabd48d2022-08-03 15:43:17 -0700135
Zev Weiss3ee959a2022-09-21 17:16:28 -0700136 fs::path dtorPath = i2cBusPath(params.bus) / "delete_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700137 std::ofstream dtor(dtorPath);
138 if (!dtor.good())
139 {
140 std::cerr << "Failed to open " << dtorPath << "\n";
141 return -1;
142 }
143
Zev Weiss3ee959a2022-09-21 17:16:28 -0700144 dtor << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700145 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}