| // Copyright 2023 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "runtime_probe/functions/ap_i2c.h" | |
| #include <fcntl.h> | |
| #include <linux/i2c.h> | |
| #include <linux/i2c-dev.h> | |
| #include <sys/ioctl.h> | |
| #include <unistd.h> | |
| #include <string> | |
| #include <utility> | |
| #include <base/files/scoped_file.h> | |
| #include <base/posix/eintr_wrapper.h> | |
| #include <base/strings/stringprintf.h> | |
| #include "runtime_probe/system/context.h" | |
| #include "runtime_probe/utils/file_utils.h" | |
| namespace runtime_probe { | |
| namespace { | |
| // Read data from I2C registers. | |
| std::optional<uint8_t> i2cget(int i2c_bus, int chip_addr, uint8_t data_addr) { | |
| if (i2c_bus < 0 || chip_addr < 0) { | |
| return std::nullopt; | |
| } | |
| base::FilePath i2c_filepath = | |
| GetRootedPath(base::StringPrintf("/dev/i2c-%d", i2c_bus)); | |
| base::ScopedFD fd(HANDLE_EINTR(open(i2c_filepath.value().c_str(), O_RDWR))); | |
| if (fd.get() < 0) { | |
| LOG(ERROR) << "Could not open file " << i2c_filepath; | |
| return std::nullopt; | |
| } | |
| auto* syscaller = Context::Get()->syscaller(); | |
| // If a device is busy (under control of others), the kernel will not allowed | |
| // to read data with `I2C_SLAVE` to prevent race condition, so we use | |
| // `I2C_SLAVE_FORCE` instead, which is also used by `i2cget -f`. | |
| if (syscaller->Ioctl(fd.get(), I2C_SLAVE_FORCE, chip_addr) < 0) { | |
| LOG(ERROR) << "Could not set target address to " | |
| << base::StringPrintf("0x%02x", chip_addr); | |
| return std::nullopt; | |
| } | |
| i2c_smbus_data data; | |
| i2c_smbus_ioctl_data args{.read_write = I2C_SMBUS_READ, | |
| .command = data_addr, | |
| .size = I2C_SMBUS_BYTE_DATA, | |
| .data = &data}; | |
| if (syscaller->Ioctl(fd.get(), I2C_SMBUS, &args)) { | |
| LOG(ERROR) << "Could not read byte " | |
| << base::StringPrintf("0x%02x", data_addr) << " from " | |
| << base::StringPrintf("0x%02x", chip_addr) << ": " | |
| << strerror(errno); | |
| return std::nullopt; | |
| } | |
| return data.byte; | |
| } | |
| } // namespace | |
| ApI2cFunction::DataType ApI2cFunction::EvalImpl() const { | |
| DataType result{}; | |
| std::optional<uint8_t> data = i2cget(i2c_bus_, chip_addr_, data_addr_); | |
| if (data) { | |
| VLOG(3) << "data: " << base::StringPrintf("0x%02x", *data); | |
| base::Value::Dict dict; | |
| dict.Set("data", *data); | |
| result.Append(std::move(dict)); | |
| } else { | |
| VLOG(3) << "data: (null)"; | |
| } | |
| return result; | |
| } | |
| } // namespace runtime_probe |