xref: /openbmc/dbus-sensors/src/DeviceMgmt.cpp (revision dabd48dd9ad9c05b1ad67e3540385bd69fe20239)
1*dabd48ddSZev Weiss #include <DeviceMgmt.hpp>
2*dabd48ddSZev Weiss 
3*dabd48ddSZev Weiss #include <filesystem>
4*dabd48ddSZev Weiss #include <fstream>
5*dabd48ddSZev Weiss 
6*dabd48ddSZev Weiss namespace fs = std::filesystem;
7*dabd48ddSZev Weiss 
8*dabd48ddSZev Weiss std::optional<I2CDevice> getI2CDevice(const I2CDeviceTypeMap& dtmap,
9*dabd48ddSZev Weiss                                       const SensorBaseConfigMap& cfg)
10*dabd48ddSZev Weiss {
11*dabd48ddSZev Weiss     auto findType = cfg.find("Type");
12*dabd48ddSZev Weiss     auto findBus = cfg.find("Bus");
13*dabd48ddSZev Weiss     auto findAddr = cfg.find("Address");
14*dabd48ddSZev Weiss 
15*dabd48ddSZev Weiss     if (findType == cfg.end() || findBus == cfg.end() || findAddr == cfg.end())
16*dabd48ddSZev Weiss     {
17*dabd48ddSZev Weiss         return std::nullopt;
18*dabd48ddSZev Weiss     }
19*dabd48ddSZev Weiss 
20*dabd48ddSZev Weiss     const std::string* type = std::get_if<std::string>(&findType->second);
21*dabd48ddSZev Weiss     const uint64_t* bus = std::get_if<uint64_t>(&findBus->second);
22*dabd48ddSZev Weiss     const uint64_t* addr = std::get_if<uint64_t>(&findAddr->second);
23*dabd48ddSZev Weiss 
24*dabd48ddSZev Weiss     if (type == nullptr || bus == nullptr || addr == nullptr)
25*dabd48ddSZev Weiss     {
26*dabd48ddSZev Weiss         return std::nullopt;
27*dabd48ddSZev Weiss     }
28*dabd48ddSZev Weiss 
29*dabd48ddSZev Weiss     auto findDevType = dtmap.find(type->c_str());
30*dabd48ddSZev Weiss     if (findDevType == dtmap.end())
31*dabd48ddSZev Weiss     {
32*dabd48ddSZev Weiss         return std::nullopt;
33*dabd48ddSZev Weiss     }
34*dabd48ddSZev Weiss 
35*dabd48ddSZev Weiss     return I2CDevice(findDevType->second, *bus, *addr);
36*dabd48ddSZev Weiss }
37*dabd48ddSZev Weiss 
38*dabd48ddSZev Weiss static fs::path i2cBusPath(uint64_t bus)
39*dabd48ddSZev Weiss {
40*dabd48ddSZev Weiss     return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
41*dabd48ddSZev Weiss }
42*dabd48ddSZev Weiss 
43*dabd48ddSZev Weiss static std::string deviceDirName(uint64_t bus, uint64_t address)
44*dabd48ddSZev Weiss {
45*dabd48ddSZev Weiss     std::ostringstream name;
46*dabd48ddSZev Weiss     name << bus << "-" << std::hex << std::setw(4) << std::setfill('0')
47*dabd48ddSZev Weiss          << address;
48*dabd48ddSZev Weiss     return name.str();
49*dabd48ddSZev Weiss }
50*dabd48ddSZev Weiss 
51*dabd48ddSZev Weiss bool I2CDevice::present(void) const
52*dabd48ddSZev Weiss {
53*dabd48ddSZev Weiss     fs::path path = i2cBusPath(bus) / deviceDirName(bus, address);
54*dabd48ddSZev Weiss 
55*dabd48ddSZev Weiss     if (type->createsHWMon)
56*dabd48ddSZev Weiss     {
57*dabd48ddSZev Weiss         path /= "hwmon";
58*dabd48ddSZev Weiss     }
59*dabd48ddSZev Weiss 
60*dabd48ddSZev Weiss     // Ignore errors; anything but a clean 'true' is fine as 'false'
61*dabd48ddSZev Weiss     std::error_code ec;
62*dabd48ddSZev Weiss     return fs::exists(path, ec);
63*dabd48ddSZev Weiss }
64*dabd48ddSZev Weiss 
65*dabd48ddSZev Weiss int I2CDevice::create(void) const
66*dabd48ddSZev Weiss {
67*dabd48ddSZev Weiss     // If it's already instantiated, there's nothing we need to do.
68*dabd48ddSZev Weiss     if (present())
69*dabd48ddSZev Weiss     {
70*dabd48ddSZev Weiss         return 0;
71*dabd48ddSZev Weiss     }
72*dabd48ddSZev Weiss 
73*dabd48ddSZev Weiss     // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
74*dabd48ddSZev Weiss     fs::path ctorPath = i2cBusPath(bus) / "new_device";
75*dabd48ddSZev Weiss     std::ofstream ctor(ctorPath);
76*dabd48ddSZev Weiss     if (!ctor.good())
77*dabd48ddSZev Weiss     {
78*dabd48ddSZev Weiss         std::cerr << "Failed to open " << ctorPath << "\n";
79*dabd48ddSZev Weiss         return -1;
80*dabd48ddSZev Weiss     }
81*dabd48ddSZev Weiss 
82*dabd48ddSZev Weiss     ctor << type->name << " " << address << "\n";
83*dabd48ddSZev Weiss     ctor.flush();
84*dabd48ddSZev Weiss     if (!ctor.good())
85*dabd48ddSZev Weiss     {
86*dabd48ddSZev Weiss         std::cerr << "Failed to write to " << ctorPath << "\n";
87*dabd48ddSZev Weiss         return -1;
88*dabd48ddSZev Weiss     }
89*dabd48ddSZev Weiss 
90*dabd48ddSZev Weiss     // Check if that created the requisite sysfs directory
91*dabd48ddSZev Weiss     if (!present())
92*dabd48ddSZev Weiss     {
93*dabd48ddSZev Weiss         destroy();
94*dabd48ddSZev Weiss         return -1;
95*dabd48ddSZev Weiss     }
96*dabd48ddSZev Weiss 
97*dabd48ddSZev Weiss     return 0;
98*dabd48ddSZev Weiss }
99*dabd48ddSZev Weiss 
100*dabd48ddSZev Weiss int I2CDevice::destroy(void) const
101*dabd48ddSZev Weiss {
102*dabd48ddSZev Weiss     // No present() check on this like in create(), since it might be used to
103*dabd48ddSZev Weiss     // clean up after a device instantiation that was only partially
104*dabd48ddSZev Weiss     // successful (i.e. when present() would return false but there's still a
105*dabd48ddSZev Weiss     // dummy i2c client device to remove)
106*dabd48ddSZev Weiss 
107*dabd48ddSZev Weiss     fs::path dtorPath = i2cBusPath(bus) / "delete_device";
108*dabd48ddSZev Weiss     std::ofstream dtor(dtorPath);
109*dabd48ddSZev Weiss     if (!dtor.good())
110*dabd48ddSZev Weiss     {
111*dabd48ddSZev Weiss         std::cerr << "Failed to open " << dtorPath << "\n";
112*dabd48ddSZev Weiss         return -1;
113*dabd48ddSZev Weiss     }
114*dabd48ddSZev Weiss 
115*dabd48ddSZev Weiss     dtor << address << "\n";
116*dabd48ddSZev Weiss     dtor.flush();
117*dabd48ddSZev Weiss     if (!dtor.good())
118*dabd48ddSZev Weiss     {
119*dabd48ddSZev Weiss         std::cerr << "Failed to write to " << dtorPath << "\n";
120*dabd48ddSZev Weiss         return -1;
121*dabd48ddSZev Weiss     }
122*dabd48ddSZev Weiss 
123*dabd48ddSZev Weiss     return 0;
124*dabd48ddSZev Weiss }
125