I2C_SLAVE ioctl purpose

匿名 (未验证) 提交于 2019-12-03 03:06:01

问题:

I am writing code for implementing a simple i2c read/write function using the general linux i2c driver linux/i2c-dev.h

I am confused about the ioctl : I2C_SLAVE

The kernel documentation states as follows :

You can do plain i2c transactions by using read(2) and write(2) calls. You do not need to pass the address byte; instead, set it through ioctl I2C_SLAVE before you try to access the device

However I am using the ioctl I2C_RDWR where I again set the slave address using i2c_msg.addr.

The kernel documentation also mentions the following :

Some ioctl() calls are for administrative tasks and are handled by i2c-dev directly. Examples include I2C_SLAVE

So is it must to use the ioctl I2C_SLAVE? If so do I need to set it just once or every time I perform a read and write?

If I had an i2c device I could have just tested the code on the device and would not have bothered you guys but unfortunately I don't have one right now.

Thanks for the help.

回答1:

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) {     int i, j = 0;     int ret;     uint8_t *buf;      buf = malloc(1 + 2 * (sizeof(data) / sizeof(data[0])));     if (buf == NULL) {         return -ENOMEM;     }      buf[j ++] = regaddr;     for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {         buf[j ++] = (data[i] & 0xff00) >> 8;         buf[j ++] = data[i] & 0xff;     }      struct i2c_msg messages[] = {         {             .addr = dev,             .buf = buf,             .len = sizeof(buf) / sizeof(buf[0]),         },     };      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) {     int i, j = 0;     int ret;     uint8_t *buf;      buf = malloc(2 * (sizeof(data) / sizeof(data[0])));     if (buf == NULL) {         return -ENOMEM;     }      for (i = 0; i < (sizeof(data) / sizeof(data[0])); 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) {     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);     } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {         return i2c_ioctl_smbus_write (fd, dev, regaddr, data);     } else {         return -ENOSYS;     } }  int parse_args (uint8_t *regaddr, uint16_t *data, 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 < sizeof(data) / sizeof(data[0]); 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;     int fd;     int ret = 0;      if (argc < 3) {         usage(argc, argv);     }      data = malloc((argc - 2) * sizeof(uint16_t));     if (data == NULL) {         fprintf (stderr, "%s.\n", strerror(ENOMEM));         return -ENOMEM;     }      if (parse_args(&regaddr, data, 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; } 


回答2:

I'm not too sure if this helps because I don't use ioctl I2C_RDWR but I've been using the following code with success:

int fd; fd = open("/dev/i2c-5", O_RDWR); ioctl(fd, I2C_SLAVE_FORCE, 0x20); i2c_smbus_write_word_data(fd, ___, ___); i2c_smbus_read_word_data(fd, ___); 

All I do is set I2C_SLAVE_FORCE once at the beginning and I can read and write as much as I want to after that.

PS - This is just a code sample and obviously you should check the returns of all of these functions. I'm using this code to communicate with a digital I/O chip. The two i2c_* functions are just wrappers that call ioctl(fd, I2C_SMBUS, &args); where args is a struct i2c_smbus_ioctl_data type.



回答3:

If you use the read() and write() methods, calling ioctl with I2C_SLAVE once is enough. You can also use I2C_SLAVE_FORCE if the device is already in use.

However I haven't yet found a consistent way to read specific registers for every device using the read()/write() methods.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!