一、i2c-dev驱动分析
1.1、设备驱动注册
分析这个驱动,还是从module_init()和module_exit()开始,程序如下:
- static int __init i2c_dev_init(void)
- {
- int res;
- printk(KERN_INFO
"i2c /dev entries driver\n");
- res = register_chrdev(I2C_MAJOR,
"i2c",
&i2cdev_fops);
- if
(res)
- goto out;
- i2c_dev_class = class_create(THIS_MODULE,
"i2c-dev");
- if
(IS_ERR(i2c_dev_class))
{
- res = PTR_ERR(i2c_dev_class);
- goto out_unreg_chrdev;
- }
- /* Keep track of adapters which will be added
or removed later
*/
- res = bus_register_notifier(&i2c_bus_type,
&i2cdev_notifier);
- if
(res)
- goto out_unreg_class;
- /* Bind
to already existing adapters
right away
*/
- i2c_for_each_dev(NULL, i2cdev_attach_adapter);
- return 0;
- out_unreg_class:
- class_destroy(i2c_dev_class);
- out_unreg_chrdev:
- unregister_chrdev(I2C_MAJOR,
"i2c");
- out:
- printk(KERN_ERR
"%s: Driver Initialisation failed\n", __FILE__);
- return res;
- }
- static void __exit i2c_dev_exit(void)
- {
- bus_unregister_notifier(&i2c_bus_type,
&i2cdev_notifier);
- i2c_for_each_dev(NULL, i2cdev_detach_adapter);
- class_destroy(i2c_dev_class);
- unregister_chrdev(I2C_MAJOR,
"i2c");
- }
- module_init(i2c_dev_init);
- module_exit(i2c_dev_exit);
首先注册了i2cdev_fops操作函数集,接着注册了一个名为”i2c-dev”的class,然后又注册了一个i2cdev_notifier,i2cdev_notifier如下:
- static struct notifier_block i2cdev_notifier
= {
- .notifier_call
= i2cdev_notifier_call,
- };
- int i2cdev_notifier_call(struct notifier_block
*nb, unsigned long action,
- void *data)
- {
- struct device
*dev
= data;
- switch (action) {
- case BUS_NOTIFY_ADD_DEVICE:
- return i2cdev_attach_adapter(dev, NULL);
- case BUS_NOTIFY_DEL_DEVICE:
- return i2cdev_detach_adapter(dev, NULL);
- }
- return 0;
- }
紧接着看下i2cdev_attach_adapter函数:
- static int i2cdev_attach_adapter(struct device
*dev, void
*dummy)
- {
- struct i2c_adapter *adap;
- struct i2c_dev *i2c_dev;
- int res;
- if
(dev->type
!=
&i2c_adapter_type)
- return 0;
- adap = to_i2c_adapter(dev);
- i2c_dev = get_free_i2c_dev(adap);
- if
(IS_ERR(i2c_dev))
- return PTR_ERR(i2c_dev);
- /* register this i2c device with the driver core
*/
- i2c_dev->dev
= device_create(i2c_dev_class,
&adap->dev,
- MKDEV(I2C_MAJOR, adap->nr),
NULL,
- "i2c-%d", adap->nr);
- if
(IS_ERR(i2c_dev->dev))
{
- res = PTR_ERR(i2c_dev->dev);
- goto error;
- }
- res = device_create_file(i2c_dev->dev,
&dev_attr_name);
- if
(res)
- goto error_destroy;
- pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
- adap->name, adap->nr);
- return 0;
- error_destroy:
- device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
- error:
- return_i2c_dev(i2c_dev);
- return res;
- }
这个函数也很简单,首先调用get_free_i2c_dev()分配并初始化了一个struct i2c_dev结构,使i2c_dev->adap指向操作的adapter.之后,该i2c_dev会被链入链表i2c_dev_list中。再分别以I2C_MAJOR,、adap->nr为主次设备号创建了一个device。如果此时系统配置了udev或者是hotplug,那么就在/dev下自动创建相关的设备节点了。
i2cdev_detach_adapter函数完成相反的操作,在此省略。
所有主设备号为I2C_MAJOR的设备节点的操作函数是i2cdev_fops,它的定义如下所示:
- 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,
- };
接下来一一分析
i2cdev_fops结构体的成员。
1.2、设备打开函数--i2cdev_open
- static int i2cdev_open(struct inode
*inode, struct file
*file)
- {
- unsigned int minor
= iminor(inode);
- struct i2c_client *client;
- struct i2c_adapter *adap;
- struct i2c_dev *i2c_dev;
- i2c_dev = i2c_dev_get_by_minor(minor); //以次设备号从i2c_dev_list链表中取得i2c_dev
- if (!i2c_dev)
- return -ENODEV;
- adap = i2c_get_adapter(i2c_dev->adap->nr); //以apapter的总线号从i2c_adapter_idr中找到adapter
- if (!adap)
- return -ENODEV;
- /* This creates an anonymous i2c_client, which may later be
- * pointed
to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
- *
- * This client
is ** NEVER REGISTERED
** with the driver model
- * or I2C core It just holds
private copies of addressing
- * information
and maybe a PEC flag.
- */
- client = kzalloc(sizeof(*client), GFP_KERNEL); //分配并初始化一个i2c_client结构
- if (!client)
{
- i2c_put_adapter(adap);
- return -ENOMEM;
- }
- snprintf(client->name, I2C_NAME_SIZE,
"i2c-dev %d", adap->nr);
- client->adapter
= adap; //clinet->adapter指向操作的adapter
- file->private_data
= client; //关联到file
- return 0;
- }
注意这里分配并初始化了一个struct
i2c_client结构,但是没有注册这个clinet。此外,这个函数中还有一个比较奇怪的操作,不是在前面已经将i2c_dev->adap指向要操作的adapter么?为什么还要以adapter->nr为关键字从i2c_adapter_idr去找这个操作的adapter呢?因为调用i2c_get_adapter()从总线号nr找到操作的adapter的时候,还会增加module的引用计数,这样可以防止模块意外被释放掉,那 i2c_dev->adap->nr操作,如果i2c_dev->adap被释放掉的话,不是会引起系统崩溃么?这里因为在
i2cdev_attach_adapter()间接的增加了一次adapter的一次引用计数.如下:
- static int i2cdev_attach_adapter(struct i2c_adapter
*adap)
- {
- ......
- i2c_dev->dev
= device_create(i2c_dev_class,
&adap->dev,
- MKDEV(I2C_MAJOR, adap->nr),
- "i2c-%d", adap->nr);
- ......
- }
i2c_dev内嵌的device是以adap->dev为父结点,在device_create()中会增次adap->dev的一次引用计数。
1.3、read操作
Read操作对应的操作函数如下示:
- 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
= (struct i2c_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 %zd bytes./n",
- iminor(file->f_path.dentry->d_inode),
count);
- ret = i2c_master_recv(client,tmp,count);
- if (ret
>= 0)
- ret = copy_to_user(buf,tmp,count)?-EFAULT:ret;
- kfree(tmp);
- return ret;
- }
首先从file结构中取得struct i2c_clinet,然后在kernel分配相同长度的缓存区,随之调用i2c_master_recv()从设备中读取数据.再将读取出来的数据copy到用户空间中。I2c_master_recv()在《Linux
I2C驱动分析(二)----I2C板级设备扫描和数据传输》已经讲述。
1.4、write操作
- 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->f_path.dentry->d_inode),
count);
- ret = i2c_master_send(client, tmp, count);
- kfree(tmp);
- return ret;
- }
该操作比较简单,就是将用户空间的数据通过使用函数i2c_master_send发送到i2c
设备。i2c_master_send函数在《Linux I2C驱动分析(二)----I2C板级设备扫描和数据传输》已经讲述。
memdup_user函数完成分配存储区,把应用层的数据复制到刚分配的存储区中,具体程序如下:
- void *memdup_user(const void __user
*src, size_t
len)
- {
- void *p;
- /*
- * Always use GFP_KERNEL, since copy_from_user() can sleep
and
- * cause pagefault, which makes it pointless
to use GFP_NOFS
- * or GFP_ATOMIC.
- */
- p = kmalloc_track_caller(len, GFP_KERNEL);
- if (!p)
- return ERR_PTR(-ENOMEM);
- if (copy_from_user(p, src,
len))
{
- kfree(p);
- return ERR_PTR(-EFAULT);
- }
- return p;
- }
1.5、
ioctl函数
有人可能看出了一个问题,clinet->addr是从哪来的呢?对,在read之前应该还要有一步操作来设置
clinet->addr的值。这个过程是ioctl的操作,ioctl可以设置PEC标志,重试次数,超时时间和发送接收数据等。具体程序如下:
- 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; //设置addr
- return 0;
- case I2C_TENBIT:
- //设置10 bit地址模式
- if (arg)
- client->flags
|= I2C_M_TEN;
- else
- client->flags
&=
~I2C_M_TEN;
- return 0;
- case I2C_PEC:
- //设置传输后增加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:
- //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;
- }
读取和接收数据函数i2cdev_ioctl_rdrw(client, arg);如下:
- static noinline int i2cdev_ioctl_rdrw(struct i2c_client
*client,
- unsigned long arg)
- {
- struct i2c_rdwr_ioctl_data rdwr_arg; //包括i2c_msg和它的个数
- 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 = kmalloc(rdwr_arg.nmsgs
* sizeof(struct i2c_msg), GFP_KERNEL); //创建存储i2c_msg的内存
- if (!rdwr_pa)
- return -ENOMEM;
- if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
- rdwr_arg.nmsgs
* sizeof(struct i2c_msg)))
{
- kfree(rdwr_pa);
- return -EFAULT;
- }
- 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;
- * and don‘t
let length change either.
*/
- if ((rdwr_pa[i].len
> 8192)
||
- (rdwr_pa[i].flags
& I2C_M_RECV_LEN))
{
- 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 (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;
- }
smbus协议数据传输函数i2cdev_ioctl_smbus(client,
arg);如下:
- static noinline int i2cdev_ioctl_smbus(struct i2c_client
*client,
- unsigned long arg)
- {
- struct i2c_smbus_ioctl_data data_arg;
- union i2c_smbus_data temp;
- int datasize, res;
- //从应用层复制数据
- if (copy_from_user(&data_arg,
- (struct i2c_smbus_ioctl_data __user
*) arg,
- sizeof(struct i2c_smbus_ioctl_data)))
- return -EFAULT;
- if ((data_arg.size
!= I2C_SMBUS_BYTE)
&&
- (data_arg.size
!= I2C_SMBUS_QUICK)
&&
- (data_arg.size
!= I2C_SMBUS_BYTE_DATA)
&&
- (data_arg.size
!= I2C_SMBUS_WORD_DATA)
&&
- (data_arg.size
!= I2C_SMBUS_PROC_CALL)
&&
- (data_arg.size
!= I2C_SMBUS_BLOCK_DATA)
&&
- (data_arg.size
!= I2C_SMBUS_I2C_BLOCK_BROKEN)
&&
- (data_arg.size
!= I2C_SMBUS_I2C_BLOCK_DATA)
&&
- (data_arg.size
!= I2C_SMBUS_BLOCK_PROC_CALL))
{
- dev_dbg(&client->adapter->dev,
- "size out of range (%x) in ioctl I2C_SMBUS.\n",
- data_arg.size);
- return -EINVAL; //不符合协议,直接退出
- }
- /* Note that I2C_SMBUS_READ
and I2C_SMBUS_WRITE are 0
and 1,
- so the check is valid
if size==I2C_SMBUS_QUICK too.
*/
- if ((data_arg.read_write
!= I2C_SMBUS_READ)
&&
- (data_arg.read_write
!= I2C_SMBUS_WRITE))
{
- dev_dbg(&client->adapter->dev,
- "read_write out of range (%x) in ioctl I2C_SMBUS.\n",
- data_arg.read_write);
- return -EINVAL; //既不是读,也不是写
- }
- /* Note that command values are always
*/
- if ((data_arg.size
== I2C_SMBUS_QUICK)
||
- ((data_arg.size
== I2C_SMBUS_BYTE)
&&
- (data_arg.read_write
== I2C_SMBUS_WRITE)))
- /* These are special: we
do not use data
*/
- return i2c_smbus_xfer(client->adapter, client->addr,
- client->flags, data_arg.read_write,
- data_arg.command, data_arg.size,
NULL); //不需要读
- if (data_arg.data
==
NULL) {
- dev_dbg(&client->adapter->dev,
- "data is NULL pointer in ioctl I2C_SMBUS.\n");
- return -EINVAL;
- }
- //判断数据大小
- if ((data_arg.size
== I2C_SMBUS_BYTE_DATA)
||
- (data_arg.size
== I2C_SMBUS_BYTE))
- datasize = sizeof(data_arg.data->byte);
- else if
((data_arg.size
== I2C_SMBUS_WORD_DATA)
||
- (data_arg.size
== I2C_SMBUS_PROC_CALL))
- datasize = sizeof(data_arg.data->word);
- else /* size
== smbus block, i2c block,
or block proc.
call */
- datasize = sizeof(data_arg.data->block);
- if ((data_arg.size
== I2C_SMBUS_PROC_CALL)
||
- (data_arg.size
== I2C_SMBUS_BLOCK_PROC_CALL)
||
- (data_arg.size
== I2C_SMBUS_I2C_BLOCK_DATA)
||
- (data_arg.read_write
== I2C_SMBUS_WRITE))
{
- if (copy_from_user(&temp, data_arg.data,
datasize))
- return -EFAULT;
- }
- if (data_arg.size
== I2C_SMBUS_I2C_BLOCK_BROKEN)
{
- /* Convert old I2C block commands
to the new
- convention. This preserves binary compatibility.
*/
- data_arg.size
= I2C_SMBUS_I2C_BLOCK_DATA;
- if (data_arg.read_write
== I2C_SMBUS_READ)
- temp.block[0]
= I2C_SMBUS_BLOCK_MAX;
- }
- //smbus数据传输,2中已经讲述
- res = i2c_smbus_xfer(client->adapter, client->addr,
client->flags,
- data_arg.read_write, data_arg.command, data_arg.size,
&temp);
- if (!res
&&
((data_arg.size
== I2C_SMBUS_PROC_CALL)
||
- (data_arg.size
== I2C_SMBUS_BLOCK_PROC_CALL)
||
- (data_arg.read_write
== I2C_SMBUS_READ)))
{
- if (copy_to_user(data_arg.data,
&temp, datasize))
- return -EFAULT;
- }
- return res;
- }
二、 用户空间使用i2c_dev
对于注册的i2c适配器,用户空间可以使用它们。上面的驱动对每个适配器生成一个主设备号为89的设备节点,实现了文件操作接口,用户空间可以通过i2c设备节点访问i2c适配器。适配器的编号从0开始,和适配器的设备节点的次设备号相同。i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。
2.1 前期准备
为了在用户空间的程序当中操作i2c适配器,必须在程序中包含以下两句:
- #include<linux/i2c-dev.h>
- #include<linux/i2c.h>
这两个头文件中定义了之后需要用到的结构体和宏。然后就可以打开设备节点了。但是打开哪一个呢?因为适配器的编号并不固定。为此我们在终端中运行以下命令:
[root@hdw /]# cat /sys/class/i2c-dev/i2c-0/name
BLX GSC3280 I2C adapter
如果我们想打开第二个适配器,刚好它的编号是1,对应的设备节点是/dev/i2c-1。那么可以用下面的方法打开它:
- int fd;
- if
((fd
= open("/dev/i2c-1",O_RDWR))<
0)
- {
- /* 错误处理
*/
- exit(1);
- }
打开适配器对应的设备节点,i2c-dev为打开的线程建立一个i2c_client,但是这个i2c_client并不加到i2c_adapter的client链表当中。当用户关闭设备节点时,它自动被释放。
2.2 IOCTL控制
查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令。如下:
- #define I2C_RETRIES 0x0701
/* 设置收不到ACK时的重试次数
*/
- #define I2C_TIMEOUT 0x0702 /* 设置超时时限的jiffies
*/
- #define I2C_SLAVE 0x0703 /* 设置从机地址
*/
- #define I2C_SLAVE_FORCE 0x0706 /* 强制设置从机地址
*/
- #define I2C_TENBIT 0x0704 /* 选择地址位长:=0
for 7bit
,
!= 0
for 10 bit
*/
- #define I2C_FUNCS 0x0705 /* 获取适配器支持的功能
*/
- #define I2C_RDWR 0x0707 /* Combined R/W
transfer (one STOP only)
*/
- #define I2C_PEC 0x0708 /*
!= 0
to use PEC with SMBus
*/
- #define I2C_SMBUS 0x0720 /* SMBus transfer
*/
下面进行一一解释。
1.设置重试次数
- ioctl(fd, I2C_RETRIES,
m); //这句话设置适配器收不到ACK时重试的次数为m。默认的重试次数为1。
2.设置超时
- ioctl(fd, I2C_TIMEOUT,
m);
//设置SMBus的超时时间为m,单位为jiffies。
3.设置从机地址
- ioctl(fd, I2C_SLAVE,addr);
- ioctl(fd,
I2C_SLAVE_FORCE, addr);
在调用read()和write()函数之前必须设置从机地址。这两行都可以设置从机的地址,区别是第二行无论内核中是否已有驱动在使用这个地址都会成功,第一行则只在该地址空闲的情况下成功。由于i2c-dev创建的i2c_client不加入i2c_adapter的client列表,所以不能防止其它线程使用同一地址,也不能防止驱动模块占用同一地址。
4.设置地址模式
- ioctl(file, I2C_TENBIT,
select);
//如果select不等于0选择10比特地址模式,如果等于0选择7比特模式,默认7比特。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的。
5.获取适配器功能
- ioctl(file, I2C_FUNCS, (unsignedlong
*)funcs);
//获取的适配器功能保存在funcs中。各比特的含义如:
- /* include/linux/i2c.h
*/
- #define I2C_FUNC_I2C 0x00000001
- #define I2C_FUNC_10BIT_ADDR 0x00000002
- #define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..}
*/
- #define I2C_FUNC_SMBUS_PEC 0x00000008
- #define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000
/* SMBus 2.0
*/
- #define I2C_FUNC_SMBUS_QUICK 0x00010000
- #define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
- #define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
- #define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
- #define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
- #define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
- #define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
- #define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
- #define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
- #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
- #define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000
/* I2C-like block xfer
*/
- #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000
/* w/ 1-byte reg. addr.
*/
- #define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 0x10000000
/* I2C-like block xfer
*/
- #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000
/* w/ 2-byte reg. addr.
*/
6. I2C层通信
- ioctl(file, I2C_RDWR,
(structi2c_rdwr_ioctl_data
*)msgset);
这一行代码可以使用I2C协议和设备进行通信。它进行连续的读写,中间没有间歇。只有当适配器支持I2C_FUNC_I2C此命令才有效。参数是一个指针,指向一个结构体,它的定义如:
- struct i2c_rdwr_ioctl_data
{
- struct i2c_msg __user *msgs;
/* 指向i2c_msgs数组
*/
- __u32nmsgs;
/* 消息的个数 */
- };
msgs[] 数组成员包含了指向各自缓冲区的指针。这个函数会根据是否在消息中的flags置位I2C_M_RD来对缓冲区进行读写。从机的地址以及是否使用10比特地址模式记录在每个消息中,忽略之前ioctl设置的结果。
7.设置SMBus PEC
- ioctl(file, I2C_PEC,
(long )select);
如果select不等于0选择SMBus PEC (packet error checking),等于零则关闭这个功能,默认是关闭的。
这个命令只对SMBus传输有效。这个请求只在适配器支持I2C_FUNC_SMBUS_PEC时有效;如果不支持这个命令也是安全的,它不做任何工作。
8.SMBus通信
- ioctl(file, I2C_SMBUS,
(i2c_smbus_ioctl_data*)msgset);
这个函数和I2C_RDWR类似,参数的指针指向i2c_smbus_ioctl_data类型的变量,它的定义如:
- struct i2c_smbus_ioctl_data
{
- __u8read_write;
- __u8command;
- __u32size;
- unioni2c_smbus_data __user *data;
- };
2.3、i2c_dev使用例程
要想在用户空间使用i2c适配器,首先要选择某个适配器的设备节点打开,然后才能进行通信。
2.3.1、read()/write()
通信的方式有两种,一种是使用操作普通文件的接口read()和write()。这两个函数间接调用了i2c_master_recv和i2c_master_send。但是在使用之前需要使用I2C_SLAVE设置从机地址,设置可能失败,需要检查返回值。这种通信过程进行I2C层的通信,一次只能进行一个方向的传输。
下面的程序是ARM与E2PROM芯片通信的例子,使用read()/write()与i2c设备通信:
- #include <stdio.h>
- #include <sys/ioctl.h>
- #include <fcntl.h>
- #include <linux/i2c-dev.h>
- #include <linux/i2c.h>
- #define CHIP "/dev/i2c-0"
#define CHIP_ADDR 0x50
- int main()
- {
- printf("this is i2c test/n");
- int fd =open(CHIP, O_RDWR);
- if (fd< 0)
{
- printf("open"CHIP"failed/n");
- gotoexit;
- }
- if (ioctl(fd, I2C_SLAVE_FORCE, CHIP_ADDR)
< 0)
{
- /* 设置芯片地址
*/
- printf("oictl:setslave address failed/n");
- goto close;
- }
- struct i2c_msg msg;
- unsigned char rddata;
- unsigned char rdaddr[2]
= {0, 0};
/* 将要读取的数据在芯片中的偏移量
*/
- unsigned char wrbuf[3]
= {0, 0, 0x3c};
/* 要写的数据,头两字节为偏移量
*/
- printf("inputa char you want to write to E2PROM/n");
- wrbuf[2]= getchar();
- printf("writereturn:%d, write data:%x/n", write(fd, wrbuf,
3), wrbuf[2]);
- sleep(1);
- printf("writeaddress return: %d/n",write(fd, rdaddr,
2));
/* 读取之前首先设置读取的偏移量
*/
- printf("readdata return:%d/n", read(fd,
&rddata, 1));
- printf("rddata:%c/n", rddata);
- close:
- close(fd);
- exit:
- return0;
- }
2.3.2、I2C_RDWR
还可以使用I2C_RDWR实现同样的功能,使用I2C_RDWR与I2C设备通信
- #include <stdio.h>
- #include <sys/ioctl.h>
- #include <fcntl.h>
- #include <linux/i2c-dev.h>
- #include <linux/i2c.h>
- #define CHIP "/dev/i2c-0"
- #define CHIP_ADDR 0x50
- int main()
- {
- printf("hello,this is i2c tester/n");
- int fd =open(CHIP, O_RDWR);
- if (fd< 0)
{
- printf("open"CHIP"failed/n");
- gotoexit;
- }
- struct i2c_msg msg;
- unsigned char rddata;
- unsigned char rdaddr[2]
= {0, 0};
- unsigned char wrbuf[3]
= {0, 0, 0x3c};
- printf("inputa char you want to write to E2PROM/n");
- wrbuf[2]= getchar();
- struct i2c_rdwr_ioctl_data ioctl_data;
- struct i2c_msg msgs[2];
- msgs[0].addr= CHIP_ADDR;
- msgs[0].len= 3;
- msgs[0].buf= wrbuf;
- ioctl_data.nmsgs= 1;
- ioctl_data.msgs=
&msgs[0];
- printf("ioctlwrite,return :%d/n", ioctl(fd, I2C_RDWR,
&ioctl_data));
- sleep(1);
- msgs[0].addr= CHIP_ADDR;
- msgs[0].len= 2;
- msgs[0].buf= rdaddr;
- msgs[1].addr= CHIP_ADDR;
- msgs[1].flags|= I2C_M_RD;
- msgs[1].len= 1;
- msgs[1].buf=
&rddata;
- ioctl_data.nmsgs= 1;
- ioctl_data.msgs= msgs;
- printf("ioctlwrite address, return :%d/n", ioctl(fd, I2C_RDWR,
&ioctl_data));
- ioctl_data.msgs=
&msgs[1];
- printf("ioctlread, return :%d/n", ioctl(fd, I2C_RDWR,
&ioctl_data));
- printf("rddata:%c/n", rddata);
- close:
- close(fd);
- exit:
- return0;
- }