I2C_SLAVE ioctl purpose
There are three major methods of communicating with i2c devices from userspace.
1. IOCTL I2C_RDWR
This method allows for simultaneous read/write and sending an uninterrupted sequence of message. Not all i2c devices support this method.
Before performing i/o with this method, you should check whether the device supports this method using an ioctl I2C_FUNCS
operation.
Using this method, you do not need to perform an ioctl I2C_SLAVE
operation -- it is done behind the scenes using the information embedded in the messages.
2. IOCTL SMBUS
This method of i/o is more powerful but the resulting code is more verbose. This method can be used if the device does not support the I2C_RDWR
method.
Using this method, you do need to perform an ioctl I2C_SLAVE
operation (or, if the device is busy, an I2C_SLAVE_FORCE
operation).
3. SYSFS I/O
This method uses the basic file i/o system calls read()
and write()
. Uninterrupted sequential operations are not possible using this method. This method can be used if the device does not support the I2C_RDWR
method.
Using this method, you do need to perform an ioctl I2C_SLAVE
operation (or, if the device is busy, an I2C_SLAVE_FORCE
operation).
I can't think of any situation when this method would be preferable to others, unless you need the chip to be treated like a file.
Full IOCTL Example
I haven't tested this example, but it shows the conceptual flow of writing to an i2c device.-- automatically detecting whether to use the ioctl I2C_RDWR
or smbus technique.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
int i, j = 0;
int ret;
uint8_t *buf;
// the extra byte is for the regaddr
size_t buff_size = 1 + size;
buf = malloc(buff_size);
if (buf == NULL) {
return -ENOMEM;
}
buf[j ++] = regaddr;
for (i = 0; i < size / sizeof(uint16_t); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
struct i2c_msg messages[] = {
{
.addr = dev,
.buf = buf,
.len = buff_size,
},
};
struct i2c_rdwr_ioctl_data payload = {
.msgs = messages,
.nmsgs = sizeof(messages) / sizeof(messages[0]),
};
ret = ioctl(fd, I2C_RDWR, &payload);
if (ret < 0) {
ret = -errno;
}
free (buf);
return ret;
}
int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
int i, j = 0;
int ret;
uint8_t *buf;
buf = malloc(size);
if (buf == NULL) {
return -ENOMEM;
}
for (i = 0; i < size / sizeof(uint16_t); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
struct i2c_smbus_ioctl_data payload = {
.read_write = I2C_SMBUS_WRITE,
.size = I2C_SMBUS_WORD_DATA,
.command = regaddr,
.data = (void *) buf,
};
ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
if (ret < 0)
{
ret = -errno;
goto exit;
}
ret = ioctl (fd, I2C_SMBUS, &payload);
if (ret < 0)
{
ret = -errno;
goto exit;
}
exit:
free(buf);
return ret;
}
int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
unsigned long funcs;
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
return -errno;
}
if (funcs & I2C_FUNC_I2C) {
return i2c_ioctl_write (fd, dev, regaddr, data, size);
} else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
return i2c_ioctl_smbus_write (fd, dev, regaddr, data, size);
} else {
return -ENOSYS;
}
}
int parse_args (uint8_t *regaddr, uint16_t *data, size_t size, char *argv[])
{
char *endptr;
int i;
*regaddr = (uint8_t) strtol(argv[1], &endptr, 0);
if (errno || endptr == argv[1]) {
return -1;
}
for (i = 0; i < size / sizeof(uint16_t); i ++) {
data[i] = (uint16_t) strtol(argv[i + 2], &endptr, 0);
if (errno || endptr == argv[i + 2]) {
return -1;
}
}
return 0;
}
void usage (int argc, char *argv[])
{
fprintf(stderr, "Usage: %s regaddr data [data]*\n", argv[0]);
fprintf(stderr, " regaddr The 8-bit register address to write to.\n");
fprintf(stderr, " data The 16-bit data to be written.\n");
exit(-1);
}
int main (int argc, char *argv[])
{
uint8_t regaddr;
uint16_t *data;
size_t size;
int fd;
int ret = 0;
if (argc < 3) {
usage(argc, argv);
}
size = (argc - 2) * sizeof(uint16_t);
data = malloc(size);
if (data == NULL) {
fprintf (stderr, "%s.\n", strerror(ENOMEM));
return -ENOMEM;
}
if (parse_args(®addr, data, size, argv) != 0) {
free(data);
usage(argc, argv);
}
fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
ret = i2c_write(fd, I2C_DEVICE, regaddr, data);
close(fd);
if (ret) {
fprintf (stderr, "%s.\n", strerror(-ret));
}
free(data);
return ret;
}
Invalid argument setting slave addres I2C
I2C addresses have 7 bits, they are from 0 to 127 decimal (0x00 to 0x7F hex).
You have set the I2C address to 0x94, this is not a valid I2C address.
Probably you have mis-read the datasheet and 0x94 is the 7-bit address together with the 1-bit R/nW bit. To convert this to just the 7-bit address divide by two: 0x4A.
Linux I2C-Dev IOCTL-Call produces wrong message
I found the misalignment...
my hardware was set up like this:
Notebook -> DP/VGA -> I2C-Slave
I used an Notebook which only got an DisplayPort output, converted this via an DP-to-VGA adapter and used the I2C-Interface where a simple Slave was attached.
And it looks like the DP-to-VGA adapter only could serve with Bytewide-Access to the I2C-Bus, so I set up a 'new' Laptop which has an VGA-Interface integrated and used it directly...
Which lead to an perfectly aligned transfer, like it was expected.
Trying to select an i2c address throws ioctl failed: invalid argument
Your supposition is correct, this wrapper function that you are using calls ioctl
with a pointer to your value instead of the value.
Have you tried simply (sb-unix:unix-ioctl fd cl-i2c-lli:+i2c-slave+ addr)
?
Linux - Is it necessary to register (instantiate) i2c devices before using them?
The i2c-dev driver binds to the bus (i2c_adapter), not a specific i2c device (i2c_client). So you only need to create the bus device to use i2c-dev and adding devices for the clients isn't necessary. In fact, you'll find i2c-dev will not let you use an I2C address bound to another driver unless you use the I2C_SLAVE_FORCE ioctl.
This is the opposite to the spidev driver, which binds to a specific spi slave device and not the bus as a whole. I2C predates the modern Linux device model and some things are different than is done in other places.
If you want a kernel driver to control the I2C device then there needs to be a device for the driver to bind to. The exception would be so-called "old style" I2C drivers which probe a set of addresses and bind to devices if any are found that appear to be the correct kind.
Related Topics
How to Give a Linux User Sudo Access
How to Fetch Java Version Using Single Line Command in Linux
Fata[0000] Get Http:///Var/Run/Docker.Sock/V1.17/Version: Dial Unix /Var/Run/Docker.Sock
Convert Multipage PDF to a Single Image
How to Get "Requests Per Second" for Apache in Linux
Ubuntu: Using Curl to Download an Image
Listen on a Network Port and Save Data to a Text File
How to Count Number of Unique Values of a Field in a Tab-Delimited Text File
How to Speed Up Linux Kernel Compilation
How to Monitor Data on a Serial Port in Linux
How to Add My Own Software to a Buildroot Linux Package
In Linux, What Do All the Values in the "Top" Command Mean
Prompting for User Input in Assembly Ci20 Seg Fault
How to Shell Have More Than One Job in Linux
How to Use Nohup to Run Process as a Background Process in Linux