1dabd48ddSZev Weiss #include <DeviceMgmt.hpp> 2dabd48ddSZev Weiss 3dabd48ddSZev Weiss #include <filesystem> 4dabd48ddSZev Weiss #include <fstream> 5dabd48ddSZev Weiss 6dabd48ddSZev Weiss namespace fs = std::filesystem; 7dabd48ddSZev Weiss 8*3ee959adSZev Weiss std::optional<I2CDeviceParams> 9*3ee959adSZev 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 36*3ee959adSZev 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*3ee959adSZev 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 66*3ee959adSZev Weiss I2CDevice::I2CDevice(I2CDeviceParams params) : params(params) 67*3ee959adSZev Weiss { 68*3ee959adSZev Weiss if (create() != 0) 69*3ee959adSZev Weiss { 70*3ee959adSZev Weiss throw std::runtime_error("failed to instantiate i2c device"); 71*3ee959adSZev Weiss } 72*3ee959adSZev Weiss } 73*3ee959adSZev Weiss 74*3ee959adSZev Weiss I2CDevice::~I2CDevice() 75*3ee959adSZev Weiss { 76*3ee959adSZev Weiss destroy(); 77*3ee959adSZev Weiss } 78*3ee959adSZev Weiss 79dabd48ddSZev Weiss int I2CDevice::create(void) const 80dabd48ddSZev Weiss { 81dabd48ddSZev Weiss // If it's already instantiated, there's nothing we need to do. 82*3ee959adSZev Weiss if (params.devicePresent()) 83dabd48ddSZev Weiss { 84dabd48ddSZev Weiss return 0; 85dabd48ddSZev Weiss } 86dabd48ddSZev Weiss 87dabd48ddSZev Weiss // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device' 88*3ee959adSZev Weiss fs::path ctorPath = i2cBusPath(params.bus) / "new_device"; 89dabd48ddSZev Weiss std::ofstream ctor(ctorPath); 90dabd48ddSZev Weiss if (!ctor.good()) 91dabd48ddSZev Weiss { 92dabd48ddSZev Weiss std::cerr << "Failed to open " << ctorPath << "\n"; 93dabd48ddSZev Weiss return -1; 94dabd48ddSZev Weiss } 95dabd48ddSZev Weiss 96*3ee959adSZev Weiss ctor << params.type->name << " " << params.address << "\n"; 97dabd48ddSZev Weiss ctor.flush(); 98dabd48ddSZev Weiss if (!ctor.good()) 99dabd48ddSZev Weiss { 100dabd48ddSZev Weiss std::cerr << "Failed to write to " << ctorPath << "\n"; 101dabd48ddSZev Weiss return -1; 102dabd48ddSZev Weiss } 103dabd48ddSZev Weiss 104dabd48ddSZev Weiss // Check if that created the requisite sysfs directory 105*3ee959adSZev Weiss if (!params.devicePresent()) 106dabd48ddSZev Weiss { 107dabd48ddSZev Weiss destroy(); 108dabd48ddSZev Weiss return -1; 109dabd48ddSZev Weiss } 110dabd48ddSZev Weiss 111dabd48ddSZev Weiss return 0; 112dabd48ddSZev Weiss } 113dabd48ddSZev Weiss 114dabd48ddSZev Weiss int I2CDevice::destroy(void) const 115dabd48ddSZev Weiss { 116*3ee959adSZev Weiss // No params.devicePresent() check on this like in create(), since it 117*3ee959adSZev Weiss // might be used to clean up after a device instantiation that was only 118*3ee959adSZev Weiss // partially successful (i.e. when params.devicePresent() would return 119*3ee959adSZev Weiss // false but there's still a dummy i2c client device to remove) 120dabd48ddSZev Weiss 121*3ee959adSZev Weiss fs::path dtorPath = i2cBusPath(params.bus) / "delete_device"; 122dabd48ddSZev Weiss std::ofstream dtor(dtorPath); 123dabd48ddSZev Weiss if (!dtor.good()) 124dabd48ddSZev Weiss { 125dabd48ddSZev Weiss std::cerr << "Failed to open " << dtorPath << "\n"; 126dabd48ddSZev Weiss return -1; 127dabd48ddSZev Weiss } 128dabd48ddSZev Weiss 129*3ee959adSZev Weiss dtor << params.address << "\n"; 130dabd48ddSZev Weiss dtor.flush(); 131dabd48ddSZev Weiss if (!dtor.good()) 132dabd48ddSZev Weiss { 133dabd48ddSZev Weiss std::cerr << "Failed to write to " << dtorPath << "\n"; 134dabd48ddSZev Weiss return -1; 135dabd48ddSZev Weiss } 136dabd48ddSZev Weiss 137dabd48ddSZev Weiss return 0; 138dabd48ddSZev Weiss } 139