xref: /openbmc/dbus-sensors/src/DeviceMgmt.cpp (revision 2aaf71757be3088fba8e706587d806d6ecc5981c)
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