pw_i2c_linux#
Linux userspace implementation for pw_i2c
Stable C++17
pw_i2c_linux
implements the pw_i2c
interface using the Linux userspace
i2c-dev
driver. Transfers are executed using blocking ioctl
calls.
Write+read transactions are implemented atomically using a single system call,
and a retry mechanism is used to support bus arbitration between multiple
controllers.
API reference#
-
class LinuxInitiator : public pw::i2c::Initiator#
Initiator interface implementation using the Linux userspace i2c-dev driver.
Takes exclusive control of an I2C bus device (ex. “/dev/i2c-0”). The user is responsible to open the device node prior to creating the initiator. The file descriptor is closed when the initiator object is destroyed.
The bus device must support the full I2C functionality. Users of the class are encouraged to use the
OpenI2cBus
helper to ensure the bus is valid.Access to the bus is guarded by an internal mutex, so this initiator can be safely used from multiple threads.
Public Functions
-
LinuxInitiator(int fd)#
Construct an instantiator using an open file descriptor. The file descriptor is closed during destruction.
- Parameters:
fd – [in] Valid file descriptor for an I2C device node.
Public Static Functions
-
static Result<int> OpenI2cBus(const char *bus_path)#
Open an I2C bus and validate that full I2C functionality is supported.
- Parameters:
bus_path – [in] Path to the I2C bus device node.
- Return values:
OK – The device node was opened successfully.
InvalidArgument – Failed to open the device node or to validate I2C functionality.
- Returns:
The open file descriptor on success.
-
LinuxInitiator(int fd)#
Examples#
A simple example illustrating the usage:
#include "pw_i2c/address.h"
#include "pw_i2c/device.h"
#include "pw_i2c_linux/initiator.h"
#include "pw_log/log.h"
#include "pw_result/result.h"
constexpr auto kBusPath = "/dev/i2c-0";
constexpr auto kAddress = pw::i2c::Address::SevenBit<0x42>();
pw::Result<int> result = pw::i2c::LinuxInitiator::OpenI2cBus(kBusPath);
if (!result.ok()) {
PW_LOG_ERROR("Failed to open I2C bus [%s]", kBusPath);
return result.status();
}
pw::i2c::LinuxInitiator initiator(*result);
pw::i2c::Device device(initiator, address);
// Use device to talk to address.
In real-world use cases, you may want to create an initiator singleton. This can be done by initializing a function-local static variable with a lambda:
#include <functional>
#include "pw_i2c/address.h"
#include "pw_i2c/device.h"
#include "pw_i2c/initiator.h"
#include "pw_i2c_linux/initiator.h"
#include "pw_log/log.h"
#include "pw_result/result.h"
#include "pw_status/status.h"
// Open the I2C bus and return an initiator singleton.
pw::i2c::Initiator* GetInitiator() {
static constexpr auto kBusPath = "/dev/i2c-0";
static auto* initiator = std::invoke([]() -> pw::i2c::Initiator* {
pw::Result<int> result = pw::i2c::LinuxInitiator::OpenI2cBus(kBusPath);
if (!result.ok()) {
PW_LOG_ERROR("Failed to open I2C bus [%s]", kBusPath);
return nullptr;
}
return new pw::i2c::Initiator(*result);
});
return initiator;
}
// Use the initiator from anywhere.
constexpr auto kAddress = pw::i2c::Address::SevenBit<0x42>();
auto* initiator = GetInitiator();
if (initiator == nullptr) {
PW_LOG_ERROR("I2C initiator unavailable");
return pw::Status::Internal();
}
pw::i2c::Device device(*initiator, address);
// Use device to talk to address.
Caveats#
Only 7-bit addresses are supported right now, but it should be possible to add support for 10-bit addresses with minimal changes - as long as the Linux driver supports 10-bit addresses.