15.4 Linux I2C设备(外设)驱动
I2C设备驱动要使用i2c_driver和i2c_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", ®_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