1e73bd0a1SAndrew Jeffery #include "DeviceMgmt.hpp"
2dabd48ddSZev Weiss
3eacbfdd1SEd Tanous #include "Utils.hpp"
4eacbfdd1SEd Tanous
5eacbfdd1SEd Tanous #include <cstdint>
6dabd48ddSZev Weiss #include <filesystem>
7dabd48ddSZev Weiss #include <fstream>
8eacbfdd1SEd Tanous #include <iomanip>
9eacbfdd1SEd Tanous #include <ios>
10eacbfdd1SEd Tanous #include <iostream>
11eacbfdd1SEd Tanous #include <optional>
12eacbfdd1SEd Tanous #include <sstream>
13eacbfdd1SEd Tanous #include <stdexcept>
14eacbfdd1SEd Tanous #include <string>
15eacbfdd1SEd Tanous #include <system_error>
16eacbfdd1SEd Tanous #include <variant>
17dabd48ddSZev Weiss
18dabd48ddSZev Weiss namespace fs = std::filesystem;
19dabd48ddSZev Weiss
getI2CDeviceParams(const I2CDeviceTypeMap & dtmap,const SensorBaseConfigMap & cfg)20*2aaf7175SPatrick Williams std::optional<I2CDeviceParams> getI2CDeviceParams(
21*2aaf7175SPatrick Williams const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg)
22dabd48ddSZev Weiss {
23dabd48ddSZev Weiss auto findType = cfg.find("Type");
24dabd48ddSZev Weiss auto findBus = cfg.find("Bus");
25dabd48ddSZev Weiss auto findAddr = cfg.find("Address");
26dabd48ddSZev Weiss
27dabd48ddSZev Weiss if (findType == cfg.end() || findBus == cfg.end() || findAddr == cfg.end())
28dabd48ddSZev Weiss {
29dabd48ddSZev Weiss return std::nullopt;
30dabd48ddSZev Weiss }
31dabd48ddSZev Weiss
32dabd48ddSZev Weiss const std::string* type = std::get_if<std::string>(&findType->second);
33dabd48ddSZev Weiss const uint64_t* bus = std::get_if<uint64_t>(&findBus->second);
34dabd48ddSZev Weiss const uint64_t* addr = std::get_if<uint64_t>(&findAddr->second);
35dabd48ddSZev Weiss
36dabd48ddSZev Weiss if (type == nullptr || bus == nullptr || addr == nullptr)
37dabd48ddSZev Weiss {
38dabd48ddSZev Weiss return std::nullopt;
39dabd48ddSZev Weiss }
40dabd48ddSZev Weiss
41dabd48ddSZev Weiss auto findDevType = dtmap.find(type->c_str());
42dabd48ddSZev Weiss if (findDevType == dtmap.end())
43dabd48ddSZev Weiss {
44dabd48ddSZev Weiss return std::nullopt;
45dabd48ddSZev Weiss }
46dabd48ddSZev Weiss
473ee959adSZev Weiss return I2CDeviceParams(findDevType->second, *bus, *addr);
48dabd48ddSZev Weiss }
49dabd48ddSZev Weiss
i2cBusPath(uint64_t bus)50dabd48ddSZev Weiss static fs::path i2cBusPath(uint64_t bus)
51dabd48ddSZev Weiss {
52dabd48ddSZev Weiss return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
53dabd48ddSZev Weiss }
54dabd48ddSZev Weiss
deviceDirName(uint64_t bus,uint64_t address)55dabd48ddSZev Weiss static std::string deviceDirName(uint64_t bus, uint64_t address)
56dabd48ddSZev Weiss {
57dabd48ddSZev Weiss std::ostringstream name;
58dabd48ddSZev Weiss name << bus << "-" << std::hex << std::setw(4) << std::setfill('0')
59dabd48ddSZev Weiss << address;
60dabd48ddSZev Weiss return name.str();
61dabd48ddSZev Weiss }
62dabd48ddSZev Weiss
devicePresent() const63201a1015SEd Tanous bool I2CDeviceParams::devicePresent() const
64dabd48ddSZev Weiss {
65dabd48ddSZev Weiss fs::path path = i2cBusPath(bus) / deviceDirName(bus, address);
66dabd48ddSZev Weiss
67dabd48ddSZev Weiss if (type->createsHWMon)
68dabd48ddSZev Weiss {
69dabd48ddSZev Weiss path /= "hwmon";
70dabd48ddSZev Weiss }
71dabd48ddSZev Weiss
72dabd48ddSZev Weiss // Ignore errors; anything but a clean 'true' is fine as 'false'
73dabd48ddSZev Weiss std::error_code ec;
74dabd48ddSZev Weiss return fs::exists(path, ec);
75dabd48ddSZev Weiss }
76dabd48ddSZev Weiss
deviceStatic() const77201a1015SEd Tanous bool I2CDeviceParams::deviceStatic() const
7841f49c03SZev Weiss {
7941f49c03SZev Weiss if (!devicePresent())
8041f49c03SZev Weiss {
8141f49c03SZev Weiss return false;
8241f49c03SZev Weiss }
8341f49c03SZev Weiss
8441f49c03SZev Weiss fs::path ofNode = i2cBusPath(bus) / deviceDirName(bus, address) / "of_node";
8541f49c03SZev Weiss
8641f49c03SZev Weiss // Ignore errors -- if of_node is present the device is a static DT node;
8741f49c03SZev Weiss // otherwise we can assume we created it from userspace.
8841f49c03SZev Weiss std::error_code ec;
8941f49c03SZev Weiss return fs::exists(ofNode, ec);
9041f49c03SZev Weiss }
9141f49c03SZev Weiss
I2CDevice(I2CDeviceParams params)923ee959adSZev Weiss I2CDevice::I2CDevice(I2CDeviceParams params) : params(params)
933ee959adSZev Weiss {
943ee959adSZev Weiss if (create() != 0)
953ee959adSZev Weiss {
963ee959adSZev Weiss throw std::runtime_error("failed to instantiate i2c device");
973ee959adSZev Weiss }
983ee959adSZev Weiss }
993ee959adSZev Weiss
~I2CDevice()1003ee959adSZev Weiss I2CDevice::~I2CDevice()
1013ee959adSZev Weiss {
1023ee959adSZev Weiss destroy();
1033ee959adSZev Weiss }
1043ee959adSZev Weiss
create() const105201a1015SEd Tanous int I2CDevice::create() const
106dabd48ddSZev Weiss {
107dabd48ddSZev Weiss // If it's already instantiated, there's nothing we need to do.
1083ee959adSZev Weiss if (params.devicePresent())
109dabd48ddSZev Weiss {
110dabd48ddSZev Weiss return 0;
111dabd48ddSZev Weiss }
112dabd48ddSZev Weiss
113dabd48ddSZev Weiss // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
1143ee959adSZev Weiss fs::path ctorPath = i2cBusPath(params.bus) / "new_device";
115dabd48ddSZev Weiss std::ofstream ctor(ctorPath);
116dabd48ddSZev Weiss if (!ctor.good())
117dabd48ddSZev Weiss {
118dabd48ddSZev Weiss std::cerr << "Failed to open " << ctorPath << "\n";
119dabd48ddSZev Weiss return -1;
120dabd48ddSZev Weiss }
121dabd48ddSZev Weiss
1223ee959adSZev Weiss ctor << params.type->name << " " << params.address << "\n";
123dabd48ddSZev Weiss ctor.flush();
124dabd48ddSZev Weiss if (!ctor.good())
125dabd48ddSZev Weiss {
126dabd48ddSZev Weiss std::cerr << "Failed to write to " << ctorPath << "\n";
127dabd48ddSZev Weiss return -1;
128dabd48ddSZev Weiss }
129dabd48ddSZev Weiss
130dabd48ddSZev Weiss // Check if that created the requisite sysfs directory
1313ee959adSZev Weiss if (!params.devicePresent())
132dabd48ddSZev Weiss {
133dabd48ddSZev Weiss destroy();
134dabd48ddSZev Weiss return -1;
135dabd48ddSZev Weiss }
136dabd48ddSZev Weiss
137dabd48ddSZev Weiss return 0;
138dabd48ddSZev Weiss }
139dabd48ddSZev Weiss
destroy() const140201a1015SEd Tanous int I2CDevice::destroy() const
141dabd48ddSZev Weiss {
1423ee959adSZev Weiss // No params.devicePresent() check on this like in create(), since it
1433ee959adSZev Weiss // might be used to clean up after a device instantiation that was only
1443ee959adSZev Weiss // partially successful (i.e. when params.devicePresent() would return
1453ee959adSZev Weiss // false but there's still a dummy i2c client device to remove)
146dabd48ddSZev Weiss
1473ee959adSZev Weiss fs::path dtorPath = i2cBusPath(params.bus) / "delete_device";
148dabd48ddSZev Weiss std::ofstream dtor(dtorPath);
149dabd48ddSZev Weiss if (!dtor.good())
150dabd48ddSZev Weiss {
151dabd48ddSZev Weiss std::cerr << "Failed to open " << dtorPath << "\n";
152dabd48ddSZev Weiss return -1;
153dabd48ddSZev Weiss }
154dabd48ddSZev Weiss
1553ee959adSZev Weiss dtor << params.address << "\n";
156dabd48ddSZev Weiss dtor.flush();
157dabd48ddSZev Weiss if (!dtor.good())
158dabd48ddSZev Weiss {
159dabd48ddSZev Weiss std::cerr << "Failed to write to " << dtorPath << "\n";
160dabd48ddSZev Weiss return -1;
161dabd48ddSZev Weiss }
162dabd48ddSZev Weiss
163dabd48ddSZev Weiss return 0;
164dabd48ddSZev Weiss }
165