Linux I2C_SLAVE ioctl 目的

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9974592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:33:14  来源:igfitidea点击:

I2C_SLAVE ioctl purpose

linuxdriveri2c

提问by zacurry

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

我正在编写i2c使用通用 linuxi2c驱动程序实现简单读/写功能的代码linux/i2c-dev.h

I am confused about the ioctl: I2C_SLAVE

我对以下内容感到困惑ioctlI2C_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

您可以使用 read(2) 和 write(2) 调用来执行普通的 i2c 事务。您不需要传递地址字节;相反,在尝试访问设备之前通过 ioctl I2C_SLAVE 设置它

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

但是,我正在使用ioctl I2C_RDWR我再次使用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

一些 ioctl() 调用用于管理任务,由 i2c-dev 直接处理。示例包括 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?

那么是否必须使用ioctl I2C_SLAVE? 如果是这样,我只需要设置一次还是每次执行读写操作时都需要设置它?

If I had an i2cdevice 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.

如果我有一个i2c设备,我可以在设备上测试代码,不会打扰你们,但不幸的是我现在没有。

Thanks for the help.

谢谢您的帮助。

采纳答案by Woodrow Barlow

There are three major methods of communicating with i2c devices from userspace.

从用户空间与 i2c 设备通信有三种主要方法。

1. IOCTL I2C_RDWR

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.

这种方法允许同时读/写和发送不间断的消息序列。并非所有 i2c 设备都支持此方法。

Before performing i/o with this method, you should check whether the device supports this method using an ioctl I2C_FUNCSoperation.

在使用此方法执行 i/o 之前,您应该使用 ioctlI2C_FUNCS操作检查设备是否支持此方法。

Using this method, you do notneed to perform an ioctl I2C_SLAVEoperation -- it is done behind the scenes using the information embedded in the messages.

使用这种方法,你不会需要执行一个ioctlI2C_SLAVE操作-这是使用嵌入在消息中的信息在幕后完成。

2. IOCTL SMBUS

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

这种 i/o 方法更强大,但生成的代码更冗长。如果设备不支持该方法,则可以使用该I2C_RDWR方法。

Using this method, you doneed to perform an ioctl I2C_SLAVEoperation (or, if the device is busy, an I2C_SLAVE_FORCEoperation).

使用此方法,您确实需要执行一个 ioctlI2C_SLAVE操作(或者,如果设备繁忙,则执行一个I2C_SLAVE_FORCE操作)。

3. SYSFS I/O

3. SYSFS 输入/输出

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

此方法使用基本文件 i/o 系统调用read()write(). 使用此方法无法实现不间断的顺序操作。如果设备不支持该方法,则可以使用该I2C_RDWR方法。

Using this method, you doneed to perform an ioctl I2C_SLAVEoperation (or, if the device is busy, an I2C_SLAVE_FORCEoperation).

使用此方法,您确实需要执行一个 ioctlI2C_SLAVE操作(或者,如果设备繁忙,则执行一个I2C_SLAVE_FORCE操作)。

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

完整的 IOCTL 示例

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_RDWRor smbus technique.

我没有测试过这个例子,但它显示了写入 i2c 设备的概念流程。--自动检测是否使用 ioctlI2C_RDWR或 smbus 技术。

#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(&regaddr, 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;
}

回答by 3c71

If you use the read()and write()methods, calling ioctlwith I2C_SLAVEonce is enough. You can also use I2C_SLAVE_FORCEif the device is already in use.

如果使用read()write()方法,要求ioctlI2C_SLAVE一次就够了。I2C_SLAVE_FORCE如果设备已在使用中,您也可以使用。

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

但是,我还没有找到一种一致的方法来读取使用这些read()/write()方法的每个设备的特定寄存器。

回答by Kristina

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:

我不太确定这是否有帮助,因为我不使用 ioctl I2C_RDWR 但我一直在成功使用以下代码:

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.

我所做的就是在开始时设置 I2C_SLAVE_FORCE 一次,之后我可以随意读写。

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.

PS - 这只是一个代码示例,显然您应该检查所有这些函数的返回值。我正在使用此代码与数字 I/O 芯片进行通信。两个 i2c_* 函数只是调用 ioctl(fd, I2C_SMBUS, &args); 的包装器。其中 args 是结构 i2c_smbus_ioctl_data 类型。

回答by PaperClip

For the interested, SLAVE_FORCE is used when the device in question is already being managed by a kernel driver. (i2cdetect will show UU for that address)

对于感兴趣的人,当有问题的设备已经由内核驱动程序管理时使用 SLAVE_FORCE。(i2cdetect 将显示该地址的 UU)