Linux I2C驱动--用户态驱动简单示例

Posted 99度的水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux I2C驱动--用户态驱动简单示例相关的知识,希望对你有一定的参考价值。

1. Linux内核支持I2C通用设备驱动(用户态驱动:由应用层实现对硬件的控制可以称之为用户态驱动),实现文件位于drivers/i2c/i2c-dev.c,设备文件为/dev/i2c-0

 

2. I2C通用设备驱动以字符设备注册进内核的

static const struct file_operations i2cdev_fops = {
    .owner        = THIS_MODULE,
    .llseek        = no_llseek,
    .read        = i2cdev_read,
    .write        = i2cdev_write,
    .unlocked_ioctl    = i2cdev_ioctl,
    .open        = i2cdev_open,
    .release    = i2cdev_release,
};

res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

 

3. 对设备文件进行读写时,可以调用read、write或者ioctl等方法,他们都是通过调用函数i2c_transfer来实现对I2C设备的操作的

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
    int ret;

    /* REVISIT the fault reporting model here is weak:
     *
     *  - When we get an error after receiving N bytes from a slave,
     *    there is no way to report "N".
     *
     *  - When we get a NAK after transmitting N bytes to a slave,
     *    there is no way to report "N" ... or to let the master
     *    continue executing the rest of this combined message, if
     *    that\'s the appropriate response.
     *
     *  - When for example "num" is two and we successfully complete
     *    the first message but get an error part way through the
     *    second, it\'s unclear whether that should be reported as
     *    one (discarding status on the second message) or errno
     *    (discarding status on the first one).
     */

    if (adap->algo->master_xfer) {
#ifdef DEBUG
        for (ret = 0; ret < num; ret++) {
            dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
                "len=%d%s\\n", ret, (msgs[ret].flags & I2C_M_RD)
                ? \'R\' : \'W\', msgs[ret].addr, msgs[ret].len,
                (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
        }
#endif

        if (in_atomic() || irqs_disabled()) {
            ret = mutex_trylock(&adap->bus_lock);
            if (!ret)
                /* I2C activity is ongoing. */
                return -EAGAIN;
        } else {
            mutex_lock_nested(&adap->bus_lock, adap->level);
        }

        ret = adap->algo->master_xfer(adap,msgs,num);
        mutex_unlock(&adap->bus_lock);

        return ret;
    } else {
        dev_dbg(&adap->dev, "I2C level transfers not supported\\n");
        return -EOPNOTSUPP;
    }
}

 

4. i2c_transfer通过代码可以看出,i2c_transfer 通过调用相应的 adapter 的 master_xfer 方法实现的,而 master_xfer 主要是根据 struct i2c_msg 类型的msgs来进行处理的。

struct i2c_msg {
    __u16 addr;    /* slave address            */
    __u16 flags;
#define I2C_M_TEN        0x0010    /* this is a ten bit chip address */
#define I2C_M_RD        0x0001    /* read data, from slave to master */
#define I2C_M_NOSTART        0x4000    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR    0x2000    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK        0x0800    /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN        0x0400    /* length will be first received byte */
    __u16 len;        /* msg length                */
    __u8 *buf;        /* pointer to msg data            */
};

 

5. I2C用户态驱动简单示例

(1)通过read、write实现对I2C设备的操作

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

#define SLAVE_ADDRESS 0x50
#define I2C_WRITE_FLAG 0x00
#define I2C_READ_FLAG 0x01

#define USAGE "xxx -r address length\\nxxx -w address data1 data2...\\n"

void i2c_write_bytes(int fd, unsigned char address, unsigned char* data, unsigned short len)
{    
    unsigned char* write_data = malloc(len + 1);
    
    write_data[0] = address;
    memcpy(&write_data[1], data, len);
    
    ioctl(fd, I2C_SLAVE, SLAVE_ADDRESS);
    ioctl(fd, I2C_TIMEOUT, 1);
    ioctl(fd, I2C_RETRIES, 1);
    
    write(fd, write_data, len + 1);
    
    printf("Write data success\\n");
    
    if(write_data != NULL)
    {
        free(write_data);
        write_data = NULL;
    }
}

void i2c_read_bytes(int fd, unsigned char address, unsigned char* buf, unsigned short len)
{    
    ioctl(fd, I2C_SLAVE, SLAVE_ADDRESS);
    ioctl(fd, I2C_TIMEOUT, 1);
    ioctl(fd, I2C_RETRIES, 1);
    
    write(fd, &address, 1);
    
    read(fd, buf, len);
    
    printf("buf[0] = 0x%x\\n", buf[0]);
    
    printf("Read data success\\n");
}

int main(int argc, char* argv[])
{
    int opt;
    int fd = -1;
    
    unsigned char address;
    unsigned short len = 0, i = 0;
    unsigned char buf[256] = {0};
    
    // Open device file
    fd = open("/dev/i2c-0", O_RDWR);
    if(fd < 0)
    {
        printf("Open file error\\n");
        goto Exit;
    }    
        
    while((opt = getopt(argc, argv, "w:r:")) != -1)
    {
        switch(opt)
        {
            case \'w\':
                printf("optarg = %s\\n", optarg);
                printf("optind = %d\\n", optind);
                printf("argc = %d\\n", argc);
                printf("argv[optind] = %s\\n", argv[optind]);
                
                address = (unsigned char)strtol(optarg, NULL, 0);
                printf("address = %x\\n", address);
    
                for(len = 0; optind < argc; optind++, len++)
                {
                    buf[len] = (unsigned char)strtol(argv[optind], NULL, 0);
                }
                
                printf("len = %x\\n", len);
                
                i2c_write_bytes(fd, address, buf, len);
                
                break;
                
            case \'r\':
                printf("optarg = %s\\n", optarg);
                printf("optind = %d\\n", optind);
                printf("argv[optind] = %s\\n", argv[optind]);
                
                address = (unsigned char)strtol(optarg, NULL, 0);
                printf("address = 0x%x\\n", address);
                
                len = (unsigned short)strtol(argv[optind], NULL, 0);
                printf("len = 0x%x\\n", len);
                            
                i2c_read_bytes(fd, address, buf, len);
                
                printf("Read content:\\n");
                for(i = 0; i < len; i++)
                {
                    printf("0x%x ", buf[i]);
                }
                printf("\\n");
                break;
            
            default:
                printf("Invalid parameter\\n");
                printf(USAGE);
                break;
        }
    }

Exit:
    close(fd);
    
    return 0;
}

(2)通过ioctl实现对I2C设备的操作

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

#define SLAVE_ADDRESS 0x50
#define I2C_WRITE_FLAG 0x00
#define I2C_READ_FLAG 0x01

#define USAGE "xxx -r address length\\nxxx -w address data1 data2...\\n"

void i2c_write_bytes(int fd, unsigned char address, unsigned char* data, unsigned short len)
{    
    struct i2c_rdwr_ioctl_data e2prom_write_data;
    
    e2prom_write_data.nmsgs = 1;
    e2prom_write_data.msgs = malloc(sizeof(struct i2c_msg) * e2prom_write_data.nmsgs);
    
    e2prom_write_data.msgs[0].addr = SLAVE_ADDRESS;
    e2prom_write_data.msgs[0].flags = I2C_WRITE_FLAG;
    e2prom_write_data.msgs[0].len = len + 1;//address data
    e2prom_write_data.msgs[0].buf = malloc(e2prom_write_data.msgs[0].len);
    
    e2prom_write_data.msgs[0].buf[0] = address;
    
    memcpy(&(e2prom_write_data.msgs[0].buf[1]), data, (size_t)len);
    
    // Using ioctl to write data
    ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_write_data);
    
    printf("Write data success\\n");
    
    if(e2prom_write_data.msgs != NULL)
    {
        free(e2prom_write_data.msgs);
        e2prom_write_data.msgs = NULL;
    }
}

void i2c_read_bytes(int fd, unsigned char address, unsigned char* buf, unsigned short len)
{        
    struct i2c_rdwr_ioctl_data e2prom_read_data;
    
    e2prom_read_data.nmsgs = 2;//Need writing address first, then reading
    e2prom_read_data.msgs = malloc(sizeof(struct i2c_msg) * e2prom_read_data.nmsgs);
    
    e2prom_read_data.msgs[0].addr = SLAVE_ADDRESS;
    e2prom_read_data.msgs[0].flags = I2C_WRITE_FLAG;
    e2prom_read_data.msgs[0].len = 1;
    e2prom_read_data.msgs[0].buf = malloc(e2prom_read_data.msgs[0].len);
    
    e2prom_read_data.msgs[0].buf[0] = address;
    
    e2prom_read_data.msgs[1].addr = SLAVE_ADDRESS;
    e2prom_read_data.msgs[1].flags = I2C_READ_FLAG;
    e2prom_read_data.msgs[1].len = len;
    e2prom_read_data.msgs[1].buf = malloc(e2prom_read_data.msgs[0].len);
    e2prom_read_data.msgs[1].buf[0] = 0x00;
    
    // Using ioctl to read data
    ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_read_data);
    
    printf("e2prom_read_data.msgs[1].buf[0] = 0x%x\\n", e2prom_read_data.msgs[1].buf[0]);
    
    memcpy((void*)buf, (void*)(e2prom_read_data.msgs[1].buf), (unsigned int)len);
    
    if(e2prom_read_data.msgs != NULL)
    {
        free(e2prom_read_data.msgs);
        e2prom_read_data.msgs = NULL;
    }
}

int main(int argc, char* argv[])
{
    int opt;
    int fd = -1;
    
    unsigned char address;
    unsigned short len = 0, i = 0;
    unsigned char buf[256] = {0};
    
    // Open device file
    fd = open("/dev/i2c-0", O_RDWR);
    if(fd < 0)
    {
        printf("Open file error\\n");
        goto Exit;
    }    
        
    while((opt = getopt(argc, argv, "w:r:")) != -1)
    {
        switch(opt)
        {
            case \'w\':
                printf("optarg = %s\\n", optarg);
                printf("optind = %d\\n", optind);
                printf("argc = %d\\n", argc);
                printf("argv[optind] = %s\\n", argv[optind]);
                
                address = (unsigned char)strtol(optarg, NULL, 0);
                printf("address = %x\\n", address);
                
                for(len = 0; optind < argc; optind++,len++)
                {
                    buf[len] = (unsigned char)strtol(argv[optind], NULL, 0);
                }
                
                printf("len = %x\\n", len);
                
                i2c_write_bytes(fd, address, buf, len);
                
                break;
                
            case \'r\':
                printf("optarg = %s\\n", optarg);
                printf("optind = %d\\n", optind);
                printf("argv[optind] = %s\\n", argv[optind]);
                
                address = (unsigned char)strtol(optarg, NULL, 0);
                printf("address = 0x%x\\n", address);
                
                len = (unsigned short)strtol(argv[optind], NULL, 0);
                printf("len = 0x%x\\n", len);
                            
                i2c_read_bytes(fd, address, buf, len);
                
                printf("Read content:\\n");
                for(i = 0; i < len; i++)
                {
                    printf("0x%x ", buf[i]);
                }
                printf("\\n");
                break;
            
            default:
                printf("Invalid parameter\\n");
                printf(USAGE);
                break;
        }
    }

Exit:
    close(fd);
    
    return 0;
}

 

以上是关于Linux I2C驱动--用户态驱动简单示例的主要内容,如果未能解决你的问题,请参考以下文章

11 Linux设备驱动的并发控制

linux驱动之i2c子系统mpu6050设备驱动

Linux用户态驱动设计

聊聊Linux用户态驱动设计

Linux驱动开发19-I2C子系统之客户驱动分析与移植

Smart210学习记录-----Linux i2c驱动