blob: f36b68a82592c7a94ac6ecd29637023e0021b967 [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
Patrick Williams2aaf7172024-08-16 15:20:40 -040018std::optional<I2CDeviceParams> getI2CDeviceParams(
19 const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg)
Zev Weissdabd48d2022-08-03 15:43:17 -070020{
21 auto findType = cfg.find("Type");
22 auto findBus = cfg.find("Bus");
23 auto findAddr = cfg.find("Address");
24
25 if (findType == cfg.end() || findBus == cfg.end() || findAddr == cfg.end())
26 {
27 return std::nullopt;
28 }
29
30 const std::string* type = std::get_if<std::string>(&findType->second);
31 const uint64_t* bus = std::get_if<uint64_t>(&findBus->second);
32 const uint64_t* addr = std::get_if<uint64_t>(&findAddr->second);
33
34 if (type == nullptr || bus == nullptr || addr == nullptr)
35 {
36 return std::nullopt;
37 }
38
39 auto findDevType = dtmap.find(type->c_str());
40 if (findDevType == dtmap.end())
41 {
42 return std::nullopt;
43 }
44
Zev Weiss3ee959a2022-09-21 17:16:28 -070045 return I2CDeviceParams(findDevType->second, *bus, *addr);
Zev Weissdabd48d2022-08-03 15:43:17 -070046}
47
Ed Tanous2e466962025-01-30 10:59:49 -080048static std::filesystem::path i2cBusPath(uint64_t bus)
Zev Weissdabd48d2022-08-03 15:43:17 -070049{
50 return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
51}
52
53static std::string deviceDirName(uint64_t bus, uint64_t address)
54{
55 std::ostringstream name;
56 name << bus << "-" << std::hex << std::setw(4) << std::setfill('0')
57 << address;
58 return name.str();
59}
60
Ed Tanous201a1012024-04-03 18:07:28 -070061bool I2CDeviceParams::devicePresent() const
Zev Weissdabd48d2022-08-03 15:43:17 -070062{
Ed Tanous2e466962025-01-30 10:59:49 -080063 std::filesystem::path path = i2cBusPath(bus) / deviceDirName(bus, address);
Zev Weissdabd48d2022-08-03 15:43:17 -070064
65 if (type->createsHWMon)
66 {
67 path /= "hwmon";
68 }
69
70 // Ignore errors; anything but a clean 'true' is fine as 'false'
71 std::error_code ec;
Ed Tanous2e466962025-01-30 10:59:49 -080072 return std::filesystem::exists(path, ec);
Zev Weissdabd48d2022-08-03 15:43:17 -070073}
74
Ed Tanous201a1012024-04-03 18:07:28 -070075bool I2CDeviceParams::deviceStatic() const
Zev Weiss41f49c02022-09-21 17:27:18 -070076{
77 if (!devicePresent())
78 {
79 return false;
80 }
81
Ed Tanous2e466962025-01-30 10:59:49 -080082 std::filesystem::path ofNode =
83 i2cBusPath(bus) / deviceDirName(bus, address) / "of_node";
Zev Weiss41f49c02022-09-21 17:27:18 -070084
85 // Ignore errors -- if of_node is present the device is a static DT node;
86 // otherwise we can assume we created it from userspace.
87 std::error_code ec;
Ed Tanous2e466962025-01-30 10:59:49 -080088 return std::filesystem::exists(ofNode, ec);
Zev Weiss41f49c02022-09-21 17:27:18 -070089}
90
Zev Weiss3ee959a2022-09-21 17:16:28 -070091I2CDevice::I2CDevice(I2CDeviceParams params) : params(params)
92{
93 if (create() != 0)
94 {
95 throw std::runtime_error("failed to instantiate i2c device");
96 }
97}
98
99I2CDevice::~I2CDevice()
100{
101 destroy();
102}
103
Ed Tanous201a1012024-04-03 18:07:28 -0700104int I2CDevice::create() const
Zev Weissdabd48d2022-08-03 15:43:17 -0700105{
106 // If it's already instantiated, there's nothing we need to do.
Zev Weiss3ee959a2022-09-21 17:16:28 -0700107 if (params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -0700108 {
109 return 0;
110 }
111
112 // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
Ed Tanous2e466962025-01-30 10:59:49 -0800113 std::filesystem::path ctorPath = i2cBusPath(params.bus) / "new_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700114 std::ofstream ctor(ctorPath);
115 if (!ctor.good())
116 {
117 std::cerr << "Failed to open " << ctorPath << "\n";
118 return -1;
119 }
120
Zev Weiss3ee959a2022-09-21 17:16:28 -0700121 ctor << params.type->name << " " << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700122 ctor.flush();
123 if (!ctor.good())
124 {
125 std::cerr << "Failed to write to " << ctorPath << "\n";
126 return -1;
127 }
128
129 // Check if that created the requisite sysfs directory
Zev Weiss3ee959a2022-09-21 17:16:28 -0700130 if (!params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -0700131 {
132 destroy();
133 return -1;
134 }
135
136 return 0;
137}
138
Ed Tanous201a1012024-04-03 18:07:28 -0700139int I2CDevice::destroy() const
Zev Weissdabd48d2022-08-03 15:43:17 -0700140{
Zev Weiss3ee959a2022-09-21 17:16:28 -0700141 // No params.devicePresent() check on this like in create(), since it
142 // might be used to clean up after a device instantiation that was only
143 // partially successful (i.e. when params.devicePresent() would return
144 // false but there's still a dummy i2c client device to remove)
Zev Weissdabd48d2022-08-03 15:43:17 -0700145
Ed Tanous2e466962025-01-30 10:59:49 -0800146 std::filesystem::path dtorPath = i2cBusPath(params.bus) / "delete_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700147 std::ofstream dtor(dtorPath);
148 if (!dtor.good())
149 {
150 std::cerr << "Failed to open " << dtorPath << "\n";
151 return -1;
152 }
153
Zev Weiss3ee959a2022-09-21 17:16:28 -0700154 dtor << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700155 dtor.flush();
156 if (!dtor.good())
157 {
158 std::cerr << "Failed to write to " << dtorPath << "\n";
159 return -1;
160 }
161
162 return 0;
163}