blob: 4c690bab3148f2c1c0d1132d6df40763a0bbf7ab [file] [log] [blame]
Andrew Jefferye73bd0a2023-01-25 10:39:57 +10301#include "DeviceMgmt.hpp"
Zev Weissdabd48d2022-08-03 15:43:17 -07002
Ed Tanouseacbfdd2024-04-04 12:00:24 -07003#include "Utils.hpp"
4
George Liu61984352025-02-24 14:47:34 +08005#include <phosphor-logging/lg2.hpp>
6
Ed Tanouseacbfdd2024-04-04 12:00:24 -07007#include <cstdint>
Zev Weissdabd48d2022-08-03 15:43:17 -07008#include <filesystem>
9#include <fstream>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070010#include <iomanip>
11#include <ios>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070012#include <optional>
13#include <sstream>
14#include <stdexcept>
15#include <string>
16#include <system_error>
17#include <variant>
Zev Weissdabd48d2022-08-03 15:43:17 -070018
Patrick Williams2aaf7172024-08-16 15:20:40 -040019std::optional<I2CDeviceParams> getI2CDeviceParams(
20 const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg)
Zev Weissdabd48d2022-08-03 15:43:17 -070021{
22 auto findType = cfg.find("Type");
23 auto findBus = cfg.find("Bus");
24 auto findAddr = cfg.find("Address");
25
26 if (findType == cfg.end() || findBus == cfg.end() || findAddr == cfg.end())
27 {
28 return std::nullopt;
29 }
30
31 const std::string* type = std::get_if<std::string>(&findType->second);
32 const uint64_t* bus = std::get_if<uint64_t>(&findBus->second);
33 const uint64_t* addr = std::get_if<uint64_t>(&findAddr->second);
34
35 if (type == nullptr || bus == nullptr || addr == nullptr)
36 {
37 return std::nullopt;
38 }
39
40 auto findDevType = dtmap.find(type->c_str());
41 if (findDevType == dtmap.end())
42 {
43 return std::nullopt;
44 }
45
Zev Weiss3ee959a2022-09-21 17:16:28 -070046 return I2CDeviceParams(findDevType->second, *bus, *addr);
Zev Weissdabd48d2022-08-03 15:43:17 -070047}
48
Ed Tanous2e466962025-01-30 10:59:49 -080049static std::filesystem::path i2cBusPath(uint64_t bus)
Zev Weissdabd48d2022-08-03 15:43:17 -070050{
51 return {"/sys/bus/i2c/devices/i2c-" + std::to_string(bus)};
52}
53
54static std::string deviceDirName(uint64_t bus, uint64_t address)
55{
56 std::ostringstream name;
57 name << bus << "-" << std::hex << std::setw(4) << std::setfill('0')
58 << address;
59 return name.str();
60}
61
Ed Tanous201a1012024-04-03 18:07:28 -070062bool I2CDeviceParams::devicePresent() const
Zev Weissdabd48d2022-08-03 15:43:17 -070063{
Ed Tanous2e466962025-01-30 10:59:49 -080064 std::filesystem::path path = i2cBusPath(bus) / deviceDirName(bus, address);
Zev Weissdabd48d2022-08-03 15:43:17 -070065
66 if (type->createsHWMon)
67 {
68 path /= "hwmon";
69 }
70
71 // Ignore errors; anything but a clean 'true' is fine as 'false'
72 std::error_code ec;
Ed Tanous2e466962025-01-30 10:59:49 -080073 return std::filesystem::exists(path, ec);
Zev Weissdabd48d2022-08-03 15:43:17 -070074}
75
Ed Tanous201a1012024-04-03 18:07:28 -070076bool I2CDeviceParams::deviceStatic() const
Zev Weiss41f49c02022-09-21 17:27:18 -070077{
78 if (!devicePresent())
79 {
80 return false;
81 }
82
Ed Tanous2e466962025-01-30 10:59:49 -080083 std::filesystem::path ofNode =
84 i2cBusPath(bus) / deviceDirName(bus, address) / "of_node";
Zev Weiss41f49c02022-09-21 17:27:18 -070085
86 // Ignore errors -- if of_node is present the device is a static DT node;
87 // otherwise we can assume we created it from userspace.
88 std::error_code ec;
Ed Tanous2e466962025-01-30 10:59:49 -080089 return std::filesystem::exists(ofNode, ec);
Zev Weiss41f49c02022-09-21 17:27:18 -070090}
91
Zev Weiss3ee959a2022-09-21 17:16:28 -070092I2CDevice::I2CDevice(I2CDeviceParams params) : params(params)
93{
94 if (create() != 0)
95 {
96 throw std::runtime_error("failed to instantiate i2c device");
97 }
98}
99
100I2CDevice::~I2CDevice()
101{
102 destroy();
103}
104
Ed Tanous201a1012024-04-03 18:07:28 -0700105int I2CDevice::create() const
Zev Weissdabd48d2022-08-03 15:43:17 -0700106{
107 // If it's already instantiated, there's nothing we need to do.
Zev Weiss3ee959a2022-09-21 17:16:28 -0700108 if (params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -0700109 {
110 return 0;
111 }
112
113 // Try to create it: 'echo $devtype $addr > .../i2c-$bus/new_device'
Ed Tanous2e466962025-01-30 10:59:49 -0800114 std::filesystem::path ctorPath = i2cBusPath(params.bus) / "new_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700115 std::ofstream ctor(ctorPath);
116 if (!ctor.good())
117 {
George Liu61984352025-02-24 14:47:34 +0800118 lg2::error("Failed to open '{PATH}'", "PATH", ctorPath);
Zev Weissdabd48d2022-08-03 15:43:17 -0700119 return -1;
120 }
121
Zev Weiss3ee959a2022-09-21 17:16:28 -0700122 ctor << params.type->name << " " << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700123 ctor.flush();
124 if (!ctor.good())
125 {
George Liu61984352025-02-24 14:47:34 +0800126 lg2::error("Failed to write to '{PATH}'", "PATH", ctorPath);
Zev Weissdabd48d2022-08-03 15:43:17 -0700127 return -1;
128 }
129
130 // Check if that created the requisite sysfs directory
Zev Weiss3ee959a2022-09-21 17:16:28 -0700131 if (!params.devicePresent())
Zev Weissdabd48d2022-08-03 15:43:17 -0700132 {
133 destroy();
134 return -1;
135 }
136
137 return 0;
138}
139
Ed Tanous201a1012024-04-03 18:07:28 -0700140int I2CDevice::destroy() const
Zev Weissdabd48d2022-08-03 15:43:17 -0700141{
Zev Weiss3ee959a2022-09-21 17:16:28 -0700142 // No params.devicePresent() check on this like in create(), since it
143 // might be used to clean up after a device instantiation that was only
144 // partially successful (i.e. when params.devicePresent() would return
145 // false but there's still a dummy i2c client device to remove)
Zev Weissdabd48d2022-08-03 15:43:17 -0700146
Ed Tanous2e466962025-01-30 10:59:49 -0800147 std::filesystem::path dtorPath = i2cBusPath(params.bus) / "delete_device";
Zev Weissdabd48d2022-08-03 15:43:17 -0700148 std::ofstream dtor(dtorPath);
149 if (!dtor.good())
150 {
George Liu61984352025-02-24 14:47:34 +0800151 lg2::error("Failed to open '{PATH}'", "PATH", dtorPath);
Zev Weissdabd48d2022-08-03 15:43:17 -0700152 return -1;
153 }
154
Zev Weiss3ee959a2022-09-21 17:16:28 -0700155 dtor << params.address << "\n";
Zev Weissdabd48d2022-08-03 15:43:17 -0700156 dtor.flush();
157 if (!dtor.good())
158 {
George Liu61984352025-02-24 14:47:34 +0800159 lg2::error("Failed to write to '{PATH}'", "PATH", dtorPath);
Zev Weissdabd48d2022-08-03 15:43:17 -0700160 return -1;
161 }
162
163 return 0;
164}