xref: /openbmc/dbus-sensors/src/DeviceMgmt.cpp (revision 201a10157c0add72a2aac484da3d82220d545714)
1e73bd0a1SAndrew Jeffery #include "DeviceMgmt.hpp"
2dabd48ddSZev Weiss 
3dabd48ddSZev Weiss #include <filesystem>
4dabd48ddSZev Weiss #include <fstream>
5dabd48ddSZev Weiss 
6dabd48ddSZev Weiss namespace fs = std::filesystem;
7dabd48ddSZev Weiss 
83ee959adSZev Weiss std::optional<I2CDeviceParams>
93ee959adSZev Weiss     getI2CDeviceParams(const I2CDeviceTypeMap& dtmap,
10dabd48ddSZev Weiss                        const SensorBaseConfigMap& cfg)
11dabd48ddSZev Weiss {
12dabd48ddSZev Weiss     auto findType = cfg.find("Type");
13dabd48ddSZev Weiss     auto findBus = cfg.find("Bus");
14dabd48ddSZev Weiss     auto findAddr = cfg.find("Address");
15dabd48ddSZev Weiss 
16dabd48ddSZev Weiss     if (findType == cfg.end() || findBus == cfg.end() || findAddr == cfg.end())
17dabd48ddSZev Weiss     {
18dabd48ddSZev Weiss         return std::nullopt;
19dabd48ddSZev Weiss     }
20dabd48ddSZev Weiss 
21dabd48ddSZev Weiss     const std::string* type = std::get_if<std::string>(&findType->second);
22dabd48ddSZev Weiss     const uint64_t* bus = std::get_if<uint64_t>(&findBus->second);
23dabd48ddSZev Weiss     const uint64_t* addr = std::get_if<uint64_t>(&findAddr->second);
24dabd48ddSZev Weiss 
25dabd48ddSZev Weiss     if (type == nullptr || bus == nullptr || addr == nullptr)
26dabd48ddSZev Weiss     {
27dabd48ddSZev Weiss         return std::nullopt;
28dabd48ddSZev Weiss     }
29dabd48ddSZev Weiss 
30dabd48ddSZev Weiss     auto findDevType = dtmap.find(type->c_str());
31dabd48ddSZev Weiss     if (findDevType == dtmap.end())
32dabd48ddSZev Weiss     {
33dabd48ddSZev Weiss         return std::nullopt;
34dabd48ddSZev Weiss     }
35dabd48ddSZev Weiss 
363ee959adSZev Weiss     return I2CDeviceParams(findDevType->second, *bus, *addr);
37dabd48ddSZev Weiss }
38dabd48ddSZev Weiss 
39dabd48ddSZev Weiss static fs::path i2cBusPath(uint64_t bus)
40dabd48ddSZev Weiss {
41dabd48ddSZev Weiss     return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
42dabd48ddSZev Weiss }
43dabd48ddSZev Weiss 
44dabd48ddSZev Weiss static std::string deviceDirName(uint64_t bus, uint64_t address)
45dabd48ddSZev Weiss {
46dabd48ddSZev Weiss     std::ostringstream name;
47dabd48ddSZev Weiss     name << bus << "-" << std::hex << std::setw(4) << std::setfill('0')
48dabd48ddSZev Weiss          << address;
49dabd48ddSZev Weiss     return name.str();
50dabd48ddSZev Weiss }
51dabd48ddSZev Weiss 
52*201a1015SEd Tanous bool I2CDeviceParams::devicePresent() const
53dabd48ddSZev Weiss {
54dabd48ddSZev Weiss     fs::path path = i2cBusPath(bus) / deviceDirName(bus, address);
55dabd48ddSZev Weiss 
56dabd48ddSZev Weiss     if (type->createsHWMon)
57dabd48ddSZev Weiss     {
58dabd48ddSZev Weiss         path /= "hwmon";
59dabd48ddSZev Weiss     }
60dabd48ddSZev Weiss 
61dabd48ddSZev Weiss     // Ignore errors; anything but a clean 'true' is fine as 'false'
62dabd48ddSZev Weiss     std::error_code ec;
63dabd48ddSZev Weiss     return fs::exists(path, ec);
64dabd48ddSZev Weiss }
65dabd48ddSZev Weiss 
66*201a1015SEd Tanous bool I2CDeviceParams::deviceStatic() const
6741f49c03SZev Weiss {
6841f49c03SZev Weiss     if (!devicePresent())
6941f49c03SZev Weiss     {
7041f49c03SZev Weiss         return false;
7141f49c03SZev Weiss     }
7241f49c03SZev Weiss 
7341f49c03SZev Weiss     fs::path ofNode = i2cBusPath(bus) / deviceDirName(bus, address) / "of_node";
7441f49c03SZev Weiss 
7541f49c03SZev Weiss     // Ignore errors -- if of_node is present the device is a static DT node;
7641f49c03SZev Weiss     // otherwise we can assume we created it from userspace.
7741f49c03SZev Weiss     std::error_code ec;
7841f49c03SZev Weiss     return fs::exists(ofNode, ec);
7941f49c03SZev Weiss }
8041f49c03SZev Weiss 
813ee959adSZev Weiss I2CDevice::I2CDevice(I2CDeviceParams params) : params(params)
823ee959adSZev Weiss {
833ee959adSZev Weiss     if (create() != 0)
843ee959adSZev Weiss     {
853ee959adSZev Weiss         throw std::runtime_error("failed to instantiate i2c device");
863ee959adSZev Weiss     }
873ee959adSZev Weiss }
883ee959adSZev Weiss 
893ee959adSZev Weiss I2CDevice::~I2CDevice()
903ee959adSZev Weiss {
913ee959adSZev Weiss     destroy();
923ee959adSZev Weiss }
933ee959adSZev Weiss 
94*201a1015SEd Tanous int I2CDevice::create() const
95dabd48ddSZev Weiss {
96dabd48ddSZev Weiss     // If it's already instantiated, there's nothing we need to do.
973ee959adSZev Weiss     if (params.devicePresent())
98dabd48ddSZev Weiss     {
99dabd48ddSZev Weiss         return 0;
100dabd48ddSZev Weiss     }
101dabd48ddSZev Weiss 
102dabd48ddSZev Weiss     // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
1033ee959adSZev Weiss     fs::path ctorPath = i2cBusPath(params.bus) / "new_device";
104dabd48ddSZev Weiss     std::ofstream ctor(ctorPath);
105dabd48ddSZev Weiss     if (!ctor.good())
106dabd48ddSZev Weiss     {
107dabd48ddSZev Weiss         std::cerr << "Failed to open " << ctorPath << "\n";
108dabd48ddSZev Weiss         return -1;
109dabd48ddSZev Weiss     }
110dabd48ddSZev Weiss 
1113ee959adSZev Weiss     ctor << params.type->name << " " << params.address << "\n";
112dabd48ddSZev Weiss     ctor.flush();
113dabd48ddSZev Weiss     if (!ctor.good())
114dabd48ddSZev Weiss     {
115dabd48ddSZev Weiss         std::cerr << "Failed to write to " << ctorPath << "\n";
116dabd48ddSZev Weiss         return -1;
117dabd48ddSZev Weiss     }
118dabd48ddSZev Weiss 
119dabd48ddSZev Weiss     // Check if that created the requisite sysfs directory
1203ee959adSZev Weiss     if (!params.devicePresent())
121dabd48ddSZev Weiss     {
122dabd48ddSZev Weiss         destroy();
123dabd48ddSZev Weiss         return -1;
124dabd48ddSZev Weiss     }
125dabd48ddSZev Weiss 
126dabd48ddSZev Weiss     return 0;
127dabd48ddSZev Weiss }
128dabd48ddSZev Weiss 
129*201a1015SEd Tanous int I2CDevice::destroy() const
130dabd48ddSZev Weiss {
1313ee959adSZev Weiss     // No params.devicePresent() check on this like in create(), since it
1323ee959adSZev Weiss     // might be used to clean up after a device instantiation that was only
1333ee959adSZev Weiss     // partially successful (i.e. when params.devicePresent() would return
1343ee959adSZev Weiss     // false but there's still a dummy i2c client device to remove)
135dabd48ddSZev Weiss 
1363ee959adSZev Weiss     fs::path dtorPath = i2cBusPath(params.bus) / "delete_device";
137dabd48ddSZev Weiss     std::ofstream dtor(dtorPath);
138dabd48ddSZev Weiss     if (!dtor.good())
139dabd48ddSZev Weiss     {
140dabd48ddSZev Weiss         std::cerr << "Failed to open " << dtorPath << "\n";
141dabd48ddSZev Weiss         return -1;
142dabd48ddSZev Weiss     }
143dabd48ddSZev Weiss 
1443ee959adSZev Weiss     dtor << params.address << "\n";
145dabd48ddSZev Weiss     dtor.flush();
146dabd48ddSZev Weiss     if (!dtor.good())
147dabd48ddSZev Weiss     {
148dabd48ddSZev Weiss         std::cerr << "Failed to write to " << dtorPath << "\n";
149dabd48ddSZev Weiss         return -1;
150dabd48ddSZev Weiss     }
151dabd48ddSZev Weiss 
152dabd48ddSZev Weiss     return 0;
153dabd48ddSZev Weiss }
154