| i2c驱动第一步-加载
| | | 我们来讲述i2c驱动工作的第一步...加载. i2c驱动的加载有两种情况: 对于编译到内核镜像里的驱动,驱动的加载是在内核运行,也就是开机时发生的. 对于以模块方式存在的驱动,驱动的加载是在insmod时发生的. 但无论如何,其过程都是一样的.即,运行static int __init i2c_dev_init(void)函数,此函数存在于//i2c-dev.c
请大家点击这个链接i2c_dev_init,开两个窗口一起阅读,互相印证. 有用语句一:res = register_chrdev(, "i2c", &i2cdev_fops); 这个函数向内核注册了一个主设备号是I2C_MAJOR=89的字符型设备.其操作对应的file_operations结构为:i2cdev_ops.i2cdev_ops 存在于i2c-dev.c 这个驱动块. 注册这个字符型设备,就将主设备号为89的设备文件的所有操作指向了i2cdev_ops,对这个设备文件的所有操作,均由这些函数来执行. 这上步如下图: i2c设备驱动
这个初始化函数中还有另外两个有用函数: i2c_dev_class = (THIS_MODULE, "i2c-dev"); 此函数生成一个类,用于向sysfs中注册,这里不作详述. 另一个函数是: res = (&i2cdev_driver); 这里向内核注册了 i2cdev_driver,一个结构体.i2c_driver我还没搞懂,这里就先不说了. i2c驱动第二步-打开
| | | 当一个驱动被注册后,我们就可以通过创建设备节点,对设备节点进操作来操作设备. 操作一个设备节点的第一步就是打开设备文件.由第一步中可以,此时调用open函数,实则是调用i2cdev_fops中的i2cdev_fops大家可以打开这个函数对应着看. 第一个有用的语句是:i2c_dev = i2c_dev_get_by_minor(minor); 这个语句通过设备节点所对应的次设备号,去轮询 i2c_dev_list,这是一个 i2c_dev的链表,这个链表中每个i2c_dev 结构的adap成员对应一个次设备号,也就对应一个.上面所用到的函数就是通过对比次设备号,来判断并取得对应i2c从设备的i2c_dev.然后通这个结构是否为空来判断对应节点是否对应真实的设备. 第二个有用的语句是:adap = (i2c_dev->adap->nr); 通过这个函数取得要操作的设备所在的适配器结构. 实际上这个open函数的主要作用除了判断设备是否真实存在之外,还要为设备生成一个i2c_client结构.下面部分就是生成了这样一个结构,并且把这个结构的一此信息进行添充. 如图: i2c设备驱打开文件
i2c驱动第三步-写入
| | | 从前两步,我们很容易体会到,drivers/i2c/i2c-dev.c这个文件中的模块是与上层的VFS打交道,但一个驱动显然不能只与上层打交道,与上层怎么做呢?我们还要再看下去. 第三步动作不一定是写入,但当成是写入我也无碍,我们只是想看看这个驱动到底是怎么工作的,实际上写入与读出大体上也差不了多少. 显然,执行上层VFS传来的写入操作是drivers/i2c/i2c-dev.c这个文件中i2cdev_ops中的i2cdev_write函数,而当我们来看这个函数的时候,我们发现,显然它什么也没做,只不过像我们自己写字符驱动时一样,从形参的file->中找到代表这个设备的i2c_dev结构,并把用户空间传入的数据做了备分,就把问题全部推给了另一个函数. 这个倒霉的函数名字叫做.现在让我们来围观一下,这个倒霉家伙是如何辛苦劳动的。 如何所见,它似乎也很坏,只是从中找到这个设备的适配器,并把要传的信息整理一下,就又把事情传给了另一个更加倒霉的孩子。但是让我们围观那个倒霉的孩子之间,先看看现在我们的结构: i2c驱动i2c_msg
i2c驱动第三步-写入续
| | | 接上一篇本系列的第一篇是: 如我们所料,这个世界上,很多时候,一些道理是相通,我们现在看的倒霉家伙仍然不是最最倒霉的那个的,经过简单的一些辅助性处理,它又把问题推给了: adap->algo->也就是我们现在的结构中i2c_client中的i2c_adapter成员中的algo成员中的.我们当初在第二步: 中建立的i2c_client结构,当时添充其i2c_adapter成员时是通过(i2c_dev->adap->nr);来得到的。也是就说,我们根据次设备号,找到i2c_dev然后再通过它的成员的成员找到这个adapter结构。但至今为止我们还不知道这个i2c_adapter结构是发生的呢。好,我们来找找, 其实有一段话,我该写在这个系统的前面,但是忘记了没有写,现在写在这里,那就是: linux系统的i2c驱动只有非常少的代码,都放在//目录下,我们发现这个目录下面只有三个文件夹,和三个.c文件,而我们来看编译后的代码树里,真正生成了.o文件的只有5个文件。/ / i2c-dev.c 共 5个,通过我们前的讨论看来,i2c-dev.c这个文件是一个驱动中与VFS打交道的模块。i2c-core.c则是一个中间层,提供上层,底层中间的 隔离,另外三个文件我们还没有看,但显然i2c-s3c2410.c更像是与底层硬件相关的。而i2c-algo-bit.c是讨论算法的。i2c- boardinfo.c大家可以看,只有一个函数。所以我们可以三个文件都看看,找找那个 i2c_adapter添加的代码在哪里。 而实际上,我们可以上网搜一下。 最终发现添加i2c_adapter的函数有两个,而我们显然要用i2c-s3c2410.c中用了的那一个。 这两个为:()和()而根据这个文件里一段话说,前一个函数是总线号固定的时候用的,后一个则是动态的,显然s3c2410上总线号是固定的,所以那里用的是前者。到这里不得不看一下这个文件里都写了些什么,只看了下框架,发现这里是一个平台驱动的注册模块,最终向系统注册的是 ,而上面提到的那个函数则是在s3c2410_i2c_driver中的probe函数:s3c24xx_i2c_probe中被调用的。也就是说,这个i2c_client中的adapter是要在 注册时才能加进去的。这样,我们又不得不先看一下,这个模块初始化时做了些什么,因为初妈化是在probe函数之前工作的。还好这个初始化代码十分简单。就是注册了一下,这样的话,我们就可以直接来看probe函数了: 这个比较长,另开一篇。 |
|
|
|
|
|
|
|
|