blob: 1987c730b8b8dbf2fb85e834fba998cf2641e05e [file] [log] [blame]
Andrew Jefferye73bd0a2023-01-25 10:39:57 +10301#include "DeviceMgmt.hpp"
Zev Weissdabd48d2022-08-03 15:43:17 -07002
Ed Tanouseacbfdd2024-04-04 12:00:24 -07003#include "Utils.hpp"
4
5#include <cstdint>
Zev Weissdabd48d2022-08-03 15:43:17 -07006#include <filesystem>
7#include <fstream>
Ed Tanouseacbfdd2024-04-04 12:00:24 -07008#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>
Zev Weissdabd48d2022-08-03 15:43:17 -070017
18namespace fs = std::filesystem;
19
Zev Weiss3ee959a2022-09-21 17:16:28 -070020std::optional<I2CDeviceParams>
21 getI2CDeviceParams(const I2CDeviceTypeMap& dtmap,
22 const SensorBaseConfigMap& cfg)
Zev Weissdabd48d2022-08-03 15:43:17 -070023{
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
Zev Weiss3ee959a2022-09-21 17:16:28 -070048 return I2CDeviceParams(findDevType->second, *bus, *addr);
Zev Weissdabd48d2022-08-03 15:43:17 -070049}
50
51static fs::path i2cBusPath(uint64_t bus)
52{
53 return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
54}
55
56static 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
Ed Tanous201a1012024-04-03 18:07:28 -070064bool I2CDeviceParams::devicePresent() const
Zev Weissdabd48d2022-08-03 15:43:17 -070065{
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
Ed Tanous201a1012024-04-03 18:07:28 -070078bool I2CDeviceParams::deviceStatic() const
Zev Weiss41f49c02022-09-21 17:27:18 -070079{
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
Zev Weiss3ee959a2022-09-21 17:16:28 -070093I2CDevice::I2CDevice(I2CDeviceParams params) : params(params)
94{
95 if (create() != 0)
96 {
97 throw std::runtime_error("failed to instantiate i2c device");
98 }
99}
100
101I2CDevice::~I2CDevice()
102{
103 destroy();
104}
105
Ed Tanous201a1012024-04-03 18:07:28 -0700106int I2CDevice::create() const
Zev Weissdabd48d2022-08-03 15:43:17 -0700107{
108 // If it's already instantiated, there's nothing we need to do.
Zev Weiss3ee959a2022-09-21 17:16:28 -0700109 if (params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -0700110 {
111 return 0;
112 }
113
114 // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
Zev Weiss3ee959a2022-09-21 17:16:28 -0700115 fs::path ctorPath = i2cBusPath(params.bus) / "new_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700116 std::ofstream ctor(ctorPath);
117 if (!ctor.good())
118 {
119 std::cerr << "Failed to open " << ctorPath << "\n";
120 return -1;
121 }
122
Zev Weiss3ee959a2022-09-21 17:16:28 -0700123 ctor << params.type->name << " " << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700124 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
Zev Weiss3ee959a2022-09-21 17:16:28 -0700132 if (!params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -0700133 {
134 destroy();
135 return -1;
136 }
137
138 return 0;
139}
140
Ed Tanous201a1012024-04-03 18:07:28 -0700141int I2CDevice::destroy() const
Zev Weissdabd48d2022-08-03 15:43:17 -0700142{
Zev Weiss3ee959a2022-09-21 17:16:28 -0700143 // 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)
Zev Weissdabd48d2022-08-03 15:43:17 -0700147
Zev Weiss3ee959a2022-09-21 17:16:28 -0700148 fs::path dtorPath = i2cBusPath(params.bus) / "delete_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700149 std::ofstream dtor(dtorPath);
150 if (!dtor.good())
151 {
152 std::cerr << "Failed to open " << dtorPath << "\n";
153 return -1;
154 }
155
Zev Weiss3ee959a2022-09-21 17:16:28 -0700156 dtor << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700157 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}