linux内核模型---总线,设备,驱动在展讯平台上I2C设备的实例解析

来源:互联网 发布:域名注册可靠吗 编辑:程序博客网 时间:2024/06/05 09:52

        前段时间一直在阅读CSDN大牛fudan_abc写的文章,对linux内核中的总线,设备,驱动加深了了解,在此对fudan_abc深表感谢。感谢像fudan_abc这样的大牛无私的传道受业解惑,在学习linux内核模型知识时,对照着展讯7715平台上的I2C设备进行了简单的实例分析,记录了下述文字。其中原理性描述,来自于fudan_abc,请读者知悉。



 
        Linux 设备模型中三个很重要的概念就是总线,设备,驱动.即 bus,device,driver,而实际上内核中也定义了这么一些数据结构,他们是 struct bus_type,struct device,struct device_driver,这三个重要的数据结构都来自一个地方,include/linux/device.h.我们知道总线有很多种,pci总线,scsi 总线,usb 总线,所以我们会看到 Linux 内核代码中出现pci_bus_type,scsi_bus_type,usb_bus_type,他们都是 struct bus_type 类型的变量.而struct bus_type 结构中两个非常重要的成员就是 struct kset drivers 和 struct kset devices。kset 和另一个叫做 kobject 正是 Linux Kernel 2.6 中设备模型的基本元素。


        这里我们只需要知道,drivers 和 devices 的存在,让struct bus_type 与两个链表联系了起来,一个是 devices 的链表,一个是 drivers 的链表,也就是说,知道一条总线所对应的数据结构,就可以找到这条总线所关联的设备有哪些,又有哪些支持这类设备的驱动程序.
 

         而要实现这些,就要求每次出现一个设备就要向总线汇报,或者说注册,每次出现一个驱动,也要向总线汇报,或者说注册.比如系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立起一个 struct device 的变量,每一次有一个驱动程序,就要准备一个 struct device_driver 结构的变量.把这些变量统统加入相应的链表,device 插入 devices 链表,driver 插入 drivers 链表. 这样通过总线就能找到每一个设备,每一个驱动。


        struct bus_type 中为 devices 和 drivers 准备了两个链表,而代表 device 的结构体 struct device 中又有两个成员,struct bus_type *bus 和 struct device_driver *driver。同样,代表driver 的结构体 struct device_driver 同样有两个成员,struct bus_type *bus 和 struct list_head devices。struct device 和 struct device_driver 的定义和 struct bus_type 一样,在 include/linux/device.h 中。凭一种男人的直觉,可以知晓,struct device 中的 bus 记录的是这个设备连在哪条总线上,driver 记录的是这个设备用的是哪个驱动,反过来,struct device_driver 中的 bus 代表的也是这个驱动属于哪条总线,devices 记录的是这个驱动支持的那些设备,没错,是 devices(复数),而不是 device(单数),因为一个驱动程序可以支持一个或多个设备,反过来一个设备则只会绑定给一个驱动程序。




上面是理论知识,下面我们具体以展讯平台的I2C设备重力加速度传感器MC3XXX驱动为例进行实例分析:


1. 首先在Board-sp7715ga.c文件中通过sc8810_add_i2c_devices函数(其实是i2c_register_board_info函数)将指定的I2C设备(         MC3XXX_ACC_I2C_NAME, MC3XXX_ACC_I2C_ADDR---包括设备名和设备地址)申请一个 I2C struct device 结构,并且挂入I2C总线中的 devices 链表中来。
 

注意:该device挂入devices链表的操作会先于I2C设备驱动加载的操作被执行。i2c_register_board_info函数对此进行了特别说明:

/* Systems using the Linux I2C driver stack can declare tables of board info while they initialize.  This should be done in board-specific init code near arch_initcall() time, or equivalent, before any I2C adapter driver is registered.  For example, mainboard init code could define several devices, as could the init code for each daughtercard in a board stack.

 *

 * The I2C devices will be created later, after the adapter for the relevant bus has been registered.  After that moment, standard driver model tools are used to bind "new style" I2C drivers to the devices.  The bus number for any device declared using this routine is not available for dynamic allocation.

 */
 

2.具体的驱动程序在Mc3xxx.c文件中。module_init(mc3xxx_i2c_init)被称为驱动程序的初始化入口(driver initialization entry point)。当我们使用 insmod 这个命令去安装的时候(内核配置该模块为 M)或者程序启动运行到该模块的时候(内核配置为Y,该模块编译入内核),module_init()注册的函数mc3xxx_i2c_init将会被执行。

 

3. mc3xxx_i2c_init函数调用了i2c_add_driver(&mc3xxx_driver)命令,开始注册其 struct device_driver 结构,然后它去I2C总线的 devices 链表(注意:该链表在步骤1中已被更新入mc3xxx设备)中去寻找(遍历),去寻找每一个还没有绑定 driver 的设备,即 struct device 中的 struct device_driver 指针仍为空的设备,然后它会去观察这种设备的特征(mc3xxx_driver 驱动中的id_table表,第4点对此进行专门的说明),看是否是他所支持的设备,如果是,那么调用一个叫做 device_bind_driver 的函数,将设备与驱动进行绑定.换句话说,把 struct device 中的 struct device_driver driver 指向这个mc3xxx_driver,而 struct device_driver mc3xxx_driver 把 struct device 加入他的那张 struct list_head devices 链表中来.就这样,bus,device,和 driver,这三者之间或者说他们中的两两之间,就给联系上了.

 

4. 一个 driver 可以支持多个 device,一个linux系统里也有不同的device和driver。那么当发现一个 device 的时候,如何知道哪个 driver才是她的 Mr.Right 呢?这就是 id_table 的用处,让 struct mc3xxx_driver 准备一张表,里边注明该 driver 支持哪些设备,这总可以了吧.如果你这个设备属于这张表里的,那么 ok,绑定吧。
 

实际上id_table这个结构体对每一个设备来说,就相当于是她的身份证,记录了她的一些基本信息,通常我们的身份证上会记录我们的姓名,性别,出生年月,户口地址等等,而 linux各种 设备她也有她需要记录的信息,以区分她和别的 linux 设备,比如 Vendor-厂家,Product-产品,以及其他一些比如产品编号,产品的类别,遵循的协议等。
 

于是我们知道,一个 I2C driver 会把它的这张 id 表去和每一个 I2C 设备的实际情况进行比较,如果该设备的实际情况和这张表里的某一个 id 相同,准确地说,只有这许多特征都吻合,才能够把一个 I2C device 和这个 I2C driver 进行绑定,这些特征哪怕差一点也不行。

.
 

附:mc3xxx驱动结构体

static struct i2c_driver mc3xxx_driver = {

         .driver = {

                   .name = MC3XXX_DEV_NAME,

                   .owner = THIS_MODULE,

         },

         .probe    = mc3xxx_i2c_probe,

         .remove   =mc3xxx_i2c_remove,

         .suspend  = mc3xxx_i2c_suspend,

         .resume   = mc3xxx_i2c_resume,

         .id_table = mc3xxx_id,

};

在模块被正常加载后,使用lsmod命令时能查看到一个模块名为MC3XXX_DEV_NAME的模块(注意:模块名并不等于设备名)。owner 这玩艺是用来给模块计数的,每个模块都这么用,赋值总是THIS_MODULE。因为一个模块可以被多个设备共用,才会有模块计数这么一个说法.
 


0 0
原创粉丝点击