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