1*e73bd0a1SAndrew 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 523ee959adSZev Weiss bool I2CDeviceParams::devicePresent(void) 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 6641f49c03SZev Weiss bool I2CDeviceParams::deviceStatic(void) 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 94dabd48ddSZev Weiss int I2CDevice::create(void) 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 129dabd48ddSZev Weiss int I2CDevice::destroy(void) 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