书接上回,在讨论完i2c设备、i2c适配器等初始化和删除相应驱动的程序后,我们在这个小节把注意力放在file_operations里面的几个函数操作上,先贴上file_operations结构体代码,让我们先看看其包含了哪几个函数。
这个结构体想必大家对其结构都相当清晰啦,由此可见,i2c驱动为用户空间提供的操作函数包括:
1.no_llseek
2.i2cdev_read
3.i2cdev_write
4.i2cdev_ioctl
5.i2cdev_open
6.i2cdev_release
下面就对它们逐一进行分析:
1.no_llseek
从结构体i2cdev_fops 的成员名字llseek推测,其作用应该是改变当前I2C器件读写的位置,而现在正如大家所见,驱动程序并未实现这功能。
2.i2cdev_read
read函数主要做的事情有:
(1)struct i2c_client *client = file->private_data;
通过参数传入的file(在上一篇博文有阐述)而找到像对应的一个i2c设备,接下来函数就是对该设备进行操作。
(2)ret = i2c_master_recv(client, tmp, count);
其中i2c_master_recv函数在i2c-core.c 中实现,为了使本文结构更加清晰,故把跟硬件打交道的代码文件i2c-core.c 放在后面的博文分析。在这里,我们只需简单地理解为i2c_master_recv函数的作用是通过调用上文提及的i2c设备,并从其中提取count 字节的内容存到tmp 缓冲区里。
(3)ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
若无异常情况发生,就会调用咱们非常熟悉的copy_to_user 函数,这个函数在这里就不赘述了,建议还没掌握的朋友查找相关资料,或者我前面的博文都有提及过,这个是基础吖!
3.i2cdev_write
write函数又做了什么事情呢?
(1)struct i2c_client *client = file->private_data;
见上文read函数中的解释。
(2)tmp = memdup_user(buf, count);
memdup_user 函数在Util.c 中实现,源代码如下
函数的作用一目了然,就是把指针src 所指向长度为len 的内容中copy_from_user 到函数返回的指针。
(3)ret = i2c_master_send(client, tmp, count);
把缓存器tmp 里的内容发送到设备client 。
4.i2cdev_ioctl
由于i2c适配器的设备节点代表的是整条i2c总线,所以在对其进行具体的文件系统操作之前还必须指明
待访问设备的总线地址。指明地址的操作通过ioctl 系统调用完成的,它最终调用设备方法i2cdev_ioctl。ioctl函数还是采用我们非常熟悉的switch-case逻辑结构,其各种命令对应的操作分析如下
case I2C_SLAVE:
case I2C_SLAVE_FORCE: 设置从设备地址
case I2C_TENBIT: 设置7bit 地址 or 10bit 地址
case I2C_PEC: 如果I2C_PEC != 0 , to use PEC with SMBus
case I2C_FUNCS: 返回控制器算法所支持的传输种类
case I2C_RDWR: 执行i2cdev_ioctl_rdrw函数,代码如下
整个函数主要做了以下几件事
(1)copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg))
获得用户传进来的命令,并存放在rdwr_arg。
(2)copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))
把指针rdwr_pa指向结构体rdwr_arg的成员msgs。
(3)整个for循环实质就是把要进行传输的每个msg的信息copy到字符串指针data_ptrs当中。
(4)res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
i2c_transfer ()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程。至于i2c_algorithm和master_xfer等在接下来的博文介绍。
(5)while循环里把data_ptrs所存的信息统统copy_to_user。
case I2C_SMBUS: 执行i2cdev_ioctl_smbus 函数,主要调用i2c_smbus_xfer,函数在i2c-core.c 里实现。在这里我们先简单理解此函数作用为选择smbus 通信协议。
case I2C_RETRIES: 设置重发次数
case I2C_TIMEOUT: 设置超时时间,设定为 arg * 10 (ms)
呼呼~ioctl函数总算带过了,具体没有深入的地方,均是涉及到i2c-core.c 和数据结构i2c_algorithm的地方,这两个东东留待接下来的博文再来收拾。
5.i2cdev_open
最后两个函数来点轻松的,一眼扫下来,就是一些注册、初始化等工作,唯一要注意地方是这个open函数比之前所介绍的其他驱动多了一点绑定的工作,如:
client->driver = &i2cdev_driver;
client->adapter = adap;
file->private_data = client;
至于其用意,不记得的朋友建议翻看本i2c驱动系列的第一篇博文。
6.i2cdev_release
对照着open函数来看,这个release岂不是小菜一碟?
到此为止,i2c-dev.c 的驱动代码分析暂告一段落,由于本人水平所限,不能很深刻、到位的写出这份驱动代码的分析,还请各位看客多多提点。内容错漏,请严厉指出!接下来本系列的第4篇博文将讲述i2c-core.c 和数据结构i2c_algorithm,敬请关注!
本系列博文链接:
I2C驱动(1)http://blog.youkuaiyun.com/jarvis_xian/archive/2011/05/27/6449939.aspx
I2C驱动(2)http://blog.youkuaiyun.com/jarvis_xian/archive/2011/05/27/6451168.aspx
I2C驱动(3)http://blog.youkuaiyun.com/jarvis_xian/archive/2011/05/28/6452431.aspx
I2C驱动(4)http://blog.youkuaiyun.com/jarvis_xian/archive/2011/05/30/6455697.aspx