I2C_Slave Ioctl Purpose

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...

Sample Image

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



Leave a reply



Submit