第15章 Linux I2C核心、总线与设备驱动之Linux I2C设备驱动

15.4 Linux I2C设备(外设)驱动

I2C设备驱动要使用i2c_driveri2c_client数据结构并填充i2c_driver中的成员函数。

include/linux/i2c.h

struct i2c_driver {
        unsigned int class;

        /* Notifies the driver that a new bus has appeared. You should avoid
         * using this, it will be removed in a near future.
         */
        int (*attach_adapter)(struct i2c_adapter *) __deprecated;

        /* Standard driver model interfaces */
        int (*probe)(struct i2c_client *, const struct i2c_device_id *);
        int (*remove)(struct i2c_client *);

        /* driver model interfaces that don't relate to enumeration  */
        void (*shutdown)(struct i2c_client *);
        int (*suspend)(struct i2c_client *, pm_message_t mesg);
        int (*resume)(struct i2c_client *);

        /* Alert callback, for example for the SMBus alert protocol.
         * The format and meaning of the data value depends on the protocol.
         * For the SMBus alert protocol, there is a single bit of data passed
         * as the alert response's low bit ("event flag").
         */
        void (*alert)(struct i2c_client *, unsigned int data);

        /* a ioctl like command that can be used to perform specific functions
         * with the device.
         */
        int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

        struct device_driver driver;
        const struct i2c_device_id *id_table;

        /* Device detection callback for automatic device creation */
        int (*detect)(struct i2c_client *, struct i2c_board_info *);
        const unsigned short *address_list;
        struct list_head clients;

};

/**
 * struct i2c_client - represent an I2C slave device
 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
 *      I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
 * @addr: Address used on the I2C bus connected to the parent adapter.
 * @name: Indicates the type of the device, usually a chip name that's
 *      generic enough to hide second-sourcing and compatible revisions.
 * @adapter: manages the bus segment hosting this I2C device
 * @dev: Driver model device node for the slave.
 * @irq: indicates the IRQ generated by this device (if any)
 * @detected: member of an i2c_driver.clients list or i2c-core's
 *      userspace_devices list
 *
 * An i2c_client identifies a single device (i.e. chip) connected to an
 * i2c bus. The behaviour exposed to Linux is defined by the driver
 * managing the device.
 */

struct i2c_client {
        unsigned short flags;           /* div., see below              */
        unsigned short addr;            /* chip address - NOTE: 7bit    */
                                        /* addresses are stored in the  */
                                        /* _LOWER_ 7 bits               */
        char name[I2C_NAME_SIZE];
        struct i2c_adapter *adapter;    /* the adapter we sit on        */
        struct device dev;              /* the device structure         */
        int irq;                        /* irq issued by device         */
        struct list_head detected;

};

i2c_client一般被包含在设备的私有信息结构体yyy_data中,而i2c_driver则适合被定义为全局变量并初始化。

代码清单15.12 被初始化的i2c_driver

以drivers/input/misc/apanel.c为例:

static const struct i2c_device_id apanel_id[] = {
{ "fujitsu_apanel", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, apanel_id);

static struct i2c_driver apanel_driver = {
.driver = {
.name = “apanel”,
},
.probe = &apanel_probe,
.remove = &apanel_remove,
.shutdown = &apanel_shutdown,
.id_table = apanel_id,

};

15.4.1 Linux I2C设备(外设)驱动的模块加载与卸载

I2C设备(外设)驱动的模块加载函数通过I2C核心的i2c_add_driver()API函数添加i2c_driver的工作,而模块

卸载函数需要做相反的工作:通过I2C核心的i2c_del_driver()函数删除i2c_driver。

代码清单15.13 I2C外设驱动的模块加载与卸载函数模板

static int __init yyy_init(void)
 {
           return i2c_add_driver(&yyy_driver);
 }
 module_init(yyy_init);
 
 static void __exit yyy_exit(void)
{
           i2c_del_driver(&yyy_driver);
}
module_exit(yyy_exit);

15.4.2 Linux I2C设备(外设)驱动的数据传输(读写)

在I2C设备上读写数据的时序(时间顺序)且数据通常通过i2c_msg数组进行组织,最后通过i2c_transfer()函数完成,代码清单15.14所示为一个读取指定偏移offset的寄存器

代码清单15.14 I2C设备驱动的数据传输范例

uapi/linux/i2c.h


/**
 * struct i2c_msg - an I2C transaction segment beginning with START
 * @addr: Slave address, either seven or ten bits.  When this is a ten
 *      bit address, I2C_M_TEN must be set in @flags and the adapter
 *      must support I2C_FUNC_10BIT_ADDR.
 * @flags: I2C_M_RD is handled by all adapters.  No other flags may be
 *      provided unless the adapter exported the relevant I2C_FUNC_*
 *      flags through i2c_check_functionality().
 * @len: Number of data bytes in @buf being read from or written to the
 *      I2C slave address.  For read transactions where I2C_M_RECV_LEN
 *      is set, the caller guarantees that this buffer can hold up to
 *      32 bytes in addition to the initial length byte sent by the
 *      slave (plus, if used, the SMBus PEC); and this value will be
 *      incremented by the number of block data bytes received.
 * @buf: The buffer into which data is read, or from which it's written.
 *
 * An i2c_msg is the low level representation of one segment of an I2C
 * transaction.  It is visible to drivers in the @i2c_transfer() procedure,
 * to userspace from i2c-dev, and to I2C adapter drivers through the
 * @i2c_adapter.@master_xfer() method.
 *
 * Except when I2C "protocol mangling" is used, all I2C adapters implement
 * the standard rules for I2C transactions.  Each transaction begins with a
 * START.  That is followed by the slave address, and a bit encoding read
 * versus write.  Then follow all the data bytes, possibly including a byte
 * with SMBus PEC.  The transfer terminates with a NAK, or when all those
 * bytes have been transferred and ACKed.  If this is the last message in a
 * group, it is followed by a STOP.  Otherwise it is followed by the next
 * @i2c_msg transaction segment, beginning with a (repeated) START.
 *
 * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then
 * passing certain @flags may have changed those standard protocol behaviors.
 * Those flags are only for use with broken/nonconforming slaves, and with
 * adapters which are known to support the specific mangling options they
 * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).
 */
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_STOP              0x8000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART           0x4000  /* if I2C_FUNC_NOSTART */
#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                  */
};

        struct i2c_msg msg[2];
        /* 第一条消息是写消息 */
        msg[0].addr = client->addr;
        msg[0].flags = 0;
        msg[0].len = 1;
        msg[0].buf = &offs;
        /* 第二条消息是读消息 */
        msg[1].addr = client->addr;
        msg[1].flags = I2C_M_RD;
        msg[1].len = sizeof(buf);
        msg[1].buf = &buf[0];

        i2c_transfer(client->adapter, msg, 2);

15.4.3 Linux的i2c-dev.c文件分析

    代码路径:drivers/i2c/i2c-dev.c

    i2c-dev.c文件可以被看作是一个I2C设备驱动,i2c-dev.c实现的i2c_client(I2C从设备)是虚拟、临时的,主要是便于从用户空间操作I2C外设。i2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件,实现

i2c_driver的成员函数以及文件操作接口,因此i2c-dev.c的主体是“i2c_driver成员函数+字符设备驱动”。

i2c-dev.c提供的对应于用户空间要使用的文件操作接口:

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,

};

static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;

struct i2c_client *client = file->private_data;

if (count > 8192)
count = 8192;

tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;

pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
iminor(file_inode(file)), count);

ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;

}

static ssize_t i2cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
int ret;
char *tmp;
struct i2c_client *client = file->private_data;

if (count > 8192)
count = 8192;

tmp = memdup_user(buf, count);
if (IS_ERR(tmp))
return PTR_ERR(tmp);

pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
iminor(file_inode(file)), count);

ret = i2c_master_send(client, tmp, count);
kfree(tmp);
return ret;
}

i2cdev_read()、i2cdev_write()函数分别调用I2C核心的i2c_master_recv()和i2c_master_send()函数来构造一条I2C消息并引发适配器Algorithm通信函数的调用,以完成消息的传输,对应于如图15.4所示的时序。


图15.4 i2cdev_read()和i2cdev_write()函数对应的时序

大多数稍微复杂一点的I2C设备的读写流程并不对应于一条消息,往往需要两条甚至多条消息来进行一次读写周期(即如图15.5所示的重复开始位的RepStart模式),在这种情况下,在应用层仍调用read()、write()文件API来读写I2C设备,不能正确地读写。


图15.5 RepStart模式

备注:鉴于上述原因,i2c-dev.c中的i2cdev_read()和i2cdev_write()函数不具备太强的通用性,没有太大

的实用价值,只能适用于非RepStart模式的情况。对于由两条以上消息组成的读写,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWR IOCTL命令

代码清单15.15所示为i2cdev_ioctl()函数的框架。

static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
unsigned long arg)
{
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_msg *rdwr_pa;
u8 __user **data_ptrs;
int i, res;

if (copy_from_user(&rdwr_arg,
   (struct i2c_rdwr_ioctl_data __user *)arg,
   sizeof(rdwr_arg)))
return -EFAULT;


/* Put an arbitrary limit on the number of messages that can
* be sent at once */
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;

rdwr_pa = memdup_user(rdwr_arg.msgs,
      rdwr_arg.nmsgs * sizeof(struct i2c_msg));
if (IS_ERR(rdwr_pa))
return PTR_ERR(rdwr_pa);

data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}

res = 0;
for (i = 0; i < rdwr_arg.nmsgs; i++) {
/* Limit the size of the message to a sane amount */
if (rdwr_pa[i].len > 8192) {
res = -EINVAL;
break;
}

data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);
if (IS_ERR(rdwr_pa[i].buf)) {
res = PTR_ERR(rdwr_pa[i].buf);
break;
}

/*
* If the message length is received from the slave (similar
* to SMBus block read), we must ensure that the buffer will
* be large enough to cope with a message length of
* I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus
* drivers allow. The first byte in the buffer must be
* pre-filled with the number of extra bytes, which must be
* at least one to hold the message length, but can be
* greater (for example to account for a checksum byte at
* the end of the message.)
*/
if (rdwr_pa[i].flags & I2C_M_RECV_LEN) {
if (!(rdwr_pa[i].flags & I2C_M_RD) ||
    rdwr_pa[i].buf[0] < 1 ||
    rdwr_pa[i].len < rdwr_pa[i].buf[0] +
     I2C_SMBUS_BLOCK_MAX) {
res = -EINVAL;
break;
}

rdwr_pa[i].len = rdwr_pa[i].buf[0];
}
}
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}

res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
while (i-- > 0) {
if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,
rdwr_pa[i].len))
res = -EFAULT;
}
kfree(rdwr_pa[i].buf);
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data; // 获取私有数据的结构体指针
unsigned long funcs;

dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);

switch (cmd) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE: /* 设置从设备地址 */
/* NOTE:  devices set up to work with "new style" drivers
* can't use I2C_SLAVE, even when the device node is not
* bound to a driver.  Only I2C_SLAVE_FORCE will work.
*
* Setting the PEC flag here won't affect kernel drivers,
* which will be using the i2c_client node registered with
* the driver model core.  Likewise, when that client has
* the PEC flag already set, the i2c-dev driver won't see
* (or use) this setting.
*/
if ((arg > 0x3ff) ||
    (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg;
return 0;
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS:
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);

case I2C_RDWR: // 读写
return i2cdev_ioctl_rdrw(client, arg);

case I2C_SMBUS:
return i2cdev_ioctl_smbus(client, arg);

case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
/* For historical reasons, user-space sets the timeout
* value in units of 10 ms.
*/
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
/* NOTE:  returning a fault code here could cause trouble
* in buggy userspace code.  Some old kernel bugs returned
* zero in this case, and userspace code might accidentally
* have depended on that bug.
*/
return -ENOTTY;
}
return 0;
}

常用的IOCTL包括I2C_SLAVE(设置从设备地址)、I2C_RETRIES(没有收到设备ACK(响应)情况下的重试次数,默认为1)、I2C_TIMEOUT(超时)以及I2C_RDWR。

代码清单15.16和代码清单15.17为直接通过read()、write()接口和O_RDWR IOCTL读写I2C设备的例子。

代码清单15.16 直接通过read()/write()读写I2C设备

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

#define BUFF_SIZE    32

union {
    unsigned short addr;
    char bytes[2];
}tmp;

//./test /dev/i2c-0 0x18 0x20

int main(int argc, char **argv)
{
unsigned int fd;
unsigned short mem_addr;
unsigned short size;
unsigned short idx;
char buf[BUFF_SIZE];
        char cswap;

if (argc < 3) {
printf("Use:\n%s /dev/i2c-x mem_addr size\n", argv[0]);
return -1;
}

sscanf(argv[2], "%hd", &mem_addr);
sscanf(argv[3], "%hd", &size);

if (size > BUFF_SIZE)
size = BUFF_SIZE;

fd = open(argv[1], O_RDWR);

if (!fd) {
printf("Error on opening the device file\n");
return -1;
}
printf("open the device file successfully!\n");
// fd command args
ioctl(fd, I2C_SLAVE, 0x50); /* 设置EEPROM地址 */
        ioctl(fd, I2C_TIMEOUT, 1);  /* 设置超时 */
        ioctl(fd, I2C_RETRIES, 1);  /* 设置重试次数 */

for (idx = 0; idx < size; ++idx, ++mem_addr) {
tmp.addr = mem_addr;
cswap = tmp.bytes[0];
tmp.bytes[0] = tmp.bytes[1];
tmp.bytes[1] = cswap;
write(fd, &tmp.addr, 2);
read(fd, &buf[idx], 1);
}

buf[size] = '\0';
printf("Read %d char: %s\n", size, buf);
close(fd);

return 0;
}

代码清单15.17 通过O_RDWR IOCTL读写I2C设备

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <linux/i2c.h>
#include <linux/i2c-dev.h>

#if 0

/* uapi/linux/i2c-dev.h This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
    struct i2c_msg __user *msgs;    /* pointers to i2c_msgs */
    __u32 nmsgs;                    /* number of i2c_msgs */
};

#endif

int main(int argc, char *argv[])
{

struct i2c_rdwr_ioctl_data work_queue;
unsigned int idx;
unsigned int fd;
unsigned int slave_address, reg_address;
unsigned char val;
int i;
int ret;

if (argc < 4) {
printf("Usage:\n%s /dev/i2c-x start_addr reg_addr\n", argv[0]);
return -1;
}

fd = open(argv[1], O_RDWR);

if (!fd) {
printf("Error on opening the device file\n");
return -1;
}

sscanf(argv[2], "%x", &slave_address);
sscanf(argv[3], "%x", &reg_address);

work_queue.nmsgs = 2; /* 消息数量 */
work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(struct i2c_msg)); /* 分配内存空间 */
if (!work_queue.msgs) {
printf("Memory alloc error\n");
close(fd);
return -1;
}

ioctl(fd, I2C_TIMEOUT, 2);    /* 设置超时 */
ioctl(fd, I2C_RETRIES, 1);    /* 设置重试次数 */

for (i = reg_address; i < reg_address + 16; i++) {
val = i;
(work_queue.msgs[0]).len = 1;
(work_queue.msgs[0]).addr = slave_address;
(work_queue.msgs[0]).buf = &val;

(work_queue.msgs[1]).len = 1;
(work_queue.msgs[1]).flags = I2C_M_RD;
(work_queue.msgs[1]).addr = slave_address;
(work_queue.msgs[1]).buf = &val;

ret = ioctl(fd, I2C_RDWR, (unsigned long) &work_queue);
if (ret < 0)
printf("Error during I2C_RDWR ioctl with error code: %d\n", ret);
else
printf("reg:%02x val:%02x\n", i, val);
}
close(fd);

return 0;
}

备注:

使用该工具可指定读取某I2C控制器某I2C从设备某寄存器,如读取I2C控制器0上的地址为0x18的从设备,从寄存器0x20开始读:

# ./test /dev/i2c-0 0x18 0x20
reg:20 val:07
reg:21 val:00
reg:22 val:00
reg:23 val:00
reg:24 val:00
reg:25 val:00
reg:26 val:00
reg:27 val:00
reg:28 val:00
reg:29 val:00


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80563327