linux驱动那些事儿之驱动分析之前的磨刀工

来源:互联网 发布:ios上python编辑器推荐 编辑:程序博客网 时间:2024/05/17 02:45

最近一直在研究驱动,但看的过程真纠结啊,看个函数不懂,再看也不懂,尼古拉斯.赵四说过:磨刀不误砍柴工,今天就在这简短的总结一下看驱动之前需要了解的一些基础知识,和一些系统函数(这个得到驱动里面了,本篇可能会接触较少),begian:

         我们现在用的linux内核是3.1的,这里用的是sensor(传感器)的驱动,即input设备,

注意到每一个目录下面都有一个Kconfig文件和一个Makefile,这很重要,基本上,Linux内核中每一个目录下边都有一个Makefile,Makefile和Kconfig就像一个城市的地图,地图带领我们去认识一个城市,而Makefile和Kconfig则可以让我们了解这个目录下面的结构,Makefile内容:

只是粘贴了其中和我们相关的一部分,即红色字体,由此可知我们的驱动名字应该是lsm303dlhc_acc.c

obj-$(CONFIG_INPUT_KEYCHORD)                   += keychord.o

obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)     += keyspan_remote.o

obj-$(CONFIG_INPUT_KXTJ9)           += kxtj9.o

obj-$(CONFIG_INPUT_LSM303DLH_ACC)    +=lsm303dlhc_acc.o

obj-$(CONFIG_INPUT_LSM303DLH_MAG)  +=lsm303dlhc_mag.o

obj-$(CONFIG_INPUT_M68K_BEEP)                 += m68kspkr.o

obj-$(CONFIG_INPUT_MAX8925_ONKEY)       += max8925_onkey.o

obj-$(CONFIG_INPUT_MAX8997_HAPTIC)     += max8997_haptic.o

看到这里相信大家很明白了啊就,

下面看一下Kconfig的内容:也是粘贴其中相关的部分啊,红色为我们的配置信息

configINPUT_LSM303DLH_ACC

        tristate "ST LSM303DLHCAccelerometer Driver"

        depends on I2C

        select INPUT_POLLDEV

        default y

        help

          Say Yes here to enable the STLSM303DLHC Accelerometer Driver

 

          This driver will enable ST LSM303DLHCAccelerometer device.

 

          This driver can also be built as amodule. If so, the module

          will be called lsm303dlhc_compass.

 

config INPUT_LSM303DLH_MAG

       tristate "ST LSM303DLHC Magnetometer Driver"

       depends on I2C

       select INPUT_POLLDEV

       default y

       help

         Say Yes here to enable the ST LSM303DLHC Magnetometer Driver

 

         This driver will enable ST LSM303DLHC Magnetometer device.

 

         This driver can also be built as a module. If so, the module

         will be called lsm303dlhc_compass.

在Kconfig文件中,tristate表示编译选项INPUT_LSM303DLH_ACC支持在编译内核时,INPUT_LSM303DLH_ACC模块支持以模块、内建和不编译三种编译方法,默认是内建,因此,如果需要模块编译或是不编译的话,我们还需要执行make menuconfig命令来配置编译选项,使得INPUT_LSM303DLH_ACC可以以模块或者不编译。

在Makefile文件中,根据选项INPUT_LSM303DLH_ACC的值,执行不同的编译方法。

查看arch/x86/Kconfig和drivers/kconfig两个文件,在menu"Device Drivers"和endmenu之间有这样一行:

source"drivers/input/Kconfig"

这样,执行make menuconfig时,就可以配置INPUT_LSM303DLH_ACC模块的编译选项了。

drivers/input/Makefile文件中,有如下一行:

obj-$(CONFIG_INPUT_MISC)  += misc/

置编译选项:

在kernel目录下执行makemenuconfig

找到"DeviceDrivers" => "Input Device support"=> "Miscellaneousdevices"=> "ST LSM303DLHCAccelerometer Driver "选项,进行该驱动模块的具体配置,设置为y表示加载驱动,N表示不加载,M表示以模块形式动态加载。

注意,如果内核不支持动态加载模块,这里不能选择m,虽然我们在Kconfig文件中配置了INPUT_LSM303DLH_ACC选项为tristate。要支持动态加载模块选项,必须要在配置菜单中选择Enable loadable module support选项;在支持动态卸载模块选项,必须要在Enableloadable module support配置菜单项中,选择Y选项。

下面在聊聊模块机制(一般驱动模块编译出来的格式是*.ko文件,加载和卸载分别用(insmodremmod指令))

有一种感动,叫泪流满面,有一种机制,叫模块机制,十月革命一声炮响,给Linux送来了模块机制.显然,这种模块机制给那些Linux的发烧友们带来了方便,因为模块机制意味着人们可以把庞大的Linux内核划分为许许多多个小的模块,对于编写设备驱动程序的那帮家伙来说,从此以后他们可以编写设备驱动程序却不需要把她编译进内核,不用reboot机器,她只是一个模块,当你需要她的时候,你可以把她抱入怀中(insmod),当你不再需要她的时候,你可以把她一脚踢开,甚至,你可以对她咆哮:"滚吧,贱人!"(rmmod).她不能成为你的手足,只能算你的衣服.

下面看一下我们具体驱动的一部分吧

static struct i2c_driver lsm303dlhc_acc_driver= {

         .driver= {

                            .owner= THIS_MODULE,

                            .name= LSM303DLHC_ACC_DEV_NAME,

                     },

         .probe= lsm303dlhc_acc_probe,

         .remove= lsm303dlhc_acc_remove,

         .suspend= lsm303dlhc_acc_suspend,

         .resume= lsm303dlhc_acc_resume,

         .id_table= lsm303dlhc_acc_id,

};

 

static int __init lsm303dlhc_acc_init(void)

{

         pr_info("%saccelerometer driver: init\n",

                                                        LSM303DLHC_ACC_DEV_NAME);

         returni2c_add_driver(&lsm303dlhc_acc_driver);

}

 

static void __exitlsm303dlhc_acc_exit(void)

{

 

         pr_info("%saccelerometer driver exit\n",

                                                        LSM303DLHC_ACC_DEV_NAME);

 

         i2c_del_driver(&lsm303dlhc_acc_driver);

         return;

}

 

module_init(lsm303dlhc_acc_init);

module_exit(lsm303dlhc_acc_exit);

 

以上为我们驱动的一部分code,任何的驱动模块函数都要有module_init 和module_exit函数,你可以称他们为系统函数,不过实际上她们是一些宏(macro),现在你可以不用去知道它们是怎末具体实现的,,只需要知道,在Linux Kernel的世界里,你写的任何一个模块都需要使用她们来初始化或退出,或者说注册以及后来的注销.(具体的系统原理我也不清楚,记住就好,就是系统给驱动开发工程师提供的API接口)当你用module_init()为一个模块注册了之后,在你使用insmod这个命令去安装的时候,module_init()注册的函数将会被执行,而当你用rmmod这个命令去卸载一个模块的时候,module_exit()注册的函数将会被执行.module_init()被称为驱动程序的初始化入口(driver initialization entry point).

由module_init(lsm303dlhc_acc_init);

module_exit(lsm303dlhc_acc_exit);可知,真正的模块加载和卸载函数应该是lsm303dlhc_acc_init和lsm303dlhc_acc_exit,我们下面当然就从lsm303dlhc_acc_init正式开始我们的分析

 

看代码之前,需要关注这么一个问题,我需要关注的仅仅是drivers/input/misc/lsm303dlhc_acc.c这个文件?就是这样的一个文件就可以让加速传感器工作嘛?像一开始那样把这个文件比作一个小城的话,也许,城里的月光很漂亮,她能够把人的梦照亮,能够温暖人的心房.但我们真的就能厮守在这个城里,一生一世吗?

很不幸,问题远不是这样简单.外面的世界很精彩,作为加速传感器,她需要与i2c_core(此传感器是基于i2c总线的)打交道,需要与input_ core打交道,需要与内存管理单元打交道,还有内核中许许多多其它模块打交道.外面的世界很大,远比我们想象的大.

什么是i2c_core?她负责实现一些核心的功能,为别的设备驱动程序提供服务,比如申请内存,比如把驱动加入到系统,比如实现一些所有的设备都会需要的公共的函数,事实上,在传感器的世界里,一个普通的设备要正常的工作,除了要有设备本身以外,还需要有一个叫做控制器的冬冬,老外把它叫做host controller,和这个控制器相连接在一起的有另一个咚咚,她叫CPU,这个大家就很熟悉了吧, host controller本身是干什么用的呢?controller,控制器,顾名思义,用于控制,控制什么,控制所有的传感器设备的通信.通常计算机的cpu并不是直接和传感器设备打交道,而是和控制器打交道,他要对设备做什么,他会告诉控制器,而不是直接把指令发给设备,然后控制器再去负责处理这件事情,他会去指挥设备执行命令,而cpu就不用管剩下的事情,他还是该干嘛干嘛去,控制器替他去完成剩下的事情,事情办完了再通知cpu.

所以,Linux内核开发者们,专门写了一些代码,并美其名曰i2c_core.早期的Linux内核,其结构并不是如今天这般有层次感,远不像今天这般错落有致,那时候drivers/input/这个目录下边放了很多很多文件,input_core与其他各种设备的驱动程序的代码都堆砌在这里,后来,怎奈世间万千的变幻,于是在drivers/i2c/目录下面出来了一些i2c文件,以及在driver/base目录下的一些文件,就专门放一些核心的代码,比如初始化整个传感器系统,初始化host controller的代码

下面看我们那个初始化函数吧,

static int __init lsm303dlhc_acc_init(void)

{

         pr_info("%saccelerometer driver: init\n",

                                                        LSM303DLHC_ACC_DEV_NAME);

         returni2c_add_driver(&lsm303dlhc_acc_driver);

}

如上可知,很简单的code,看似只有一句是有用的,就是i2c_add_driver(&lsm303dlhc_acc_driver),这个函数是干嘛的?首先这个函数正是来自i2c_core.凡是i2c设备驱动,都要调用这个函数来向i2c_core注册,从而让i2c_core知道有这么一个设备.这就像政府规定,一对夫妻结婚要到相关部门那里去登记是一样的,我们无需知道政府是如何管理的,只需要知道去政府那里登记即可.

这样,insmod的时候, lsm303dlhc_acc_init这个函数会被调用,初始化就算完成了.于是设备就开始工作了...而当我们rmmod的时候, lsm303dlhc_acc_exit这个函数会被调用,我们发现,这个函数也很短,这里实际上也就是调用了一个函数i2c_add_driver(),她和i2c_del_driver ()是一对,完成了注销的工作,从此设备就从i2c_core中消失了.于是我们惊人的发现,编写设备驱动竟是如此的简单,驱动程序真的就这么结束了?...NO

所以我们在继续之前,先来看看这里到底有什么哲学.而这,就是伟大的Linux Kernel 2.6中的统一的设备模型.(很重要的)

我们并无意去详细介绍2.6中的设备模型,但是不懂设备模型又怎能说自己懂设备驱动呢?读代码的人,写代码的人,都要知道,1:什么是设备驱动?struct device_driver2:什么又是设备?struct device3:设备和驱动之间究竟是什么关系?设备如何与计算机主机联系起来?计算机世界里,设备有很多种类,比如PCI设备,比如usb设备,再比如SCSI设备,再比如我们这里的i2c设备.为设备联姻的是总线,struct bus_type是他把设备连入了计算机主机.但是与其说设备是嫁给了计算机主机,倒不如说设备是嫁给了设备驱动程序.很显然,在计算机世界里,无论风里雨里,陪伴着设备的正是驱动程序.

Linux设备模型中三个很重要的概念就是总线,设备,驱动.即上面的红色字体部分bus, device, driver,而实际上内核中也定义了这么一些数据结构,他们是struct bus_type,struct device,struct device_driver,这三个重要的数据结构都来自一个地方,include/linux/device.h.我们知道总线有很多种,pci总线,scsi总线,i2c总线,所以我们会看到Linux内核代码中出现pci_bus_type,scsi_bus_type,i2c_bus_type,他们都是struct bus_type类型的变量.同样的驱动和设备的结构体struct i2c_driver,struct i2c_device,其实他们就是具体到具体设备的总线、驱动、设备的变量。而struct bus_type结构中两个非常重要的成员就是struct kset drivers_kset和struct kset devices_kset和另一个叫做kobject正是Linux Kernel 2.6中设备模型的基本元素,但此处我们却不多讲,因为暂时不用去认识他们.这里我们只需要知道,drivers和devices的存在,让struct bus_type与两个链表联系了起来,一个是devices的链表即struct kset devices_kset,一个是drivers的链表即struct kset drivers_kset,他们的位置在struct bus_type->struct subsys_private下。也就是说,知道一条总线所对应的数据结构,就可以找到这条总线所关联的设备有哪些,又有哪些支持这类设备的驱动程序.

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

然而,假如计算机里只有设备却没有对应的驱动,那么设备无法工作.反过来,倘若只有驱动却没有设备,驱动也起不了任何作用.在他们遇见彼此之前,双方都如同路埂的野草,一个飘啊飘,一个摇啊摇,谁也不知道未来在哪里,只能在生命的风里飘摇.于是总线上的两张表里就慢慢的就挂上了那许多孤单的灵魂.devices开始多了,drivers开始多了,他们像是两个来自世界,devices们彼此取暖,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(单数),因为一个驱动程序可以支持一个或多个设备,反过来一个设备则只会绑定给一个驱动程序.

于是我们想知道,关于bus,关于device,关于driver,他们是如何建立联系的呢?换言之,这三个数据结构中的指针是如何被赋值的?绝对不可能发生的事情是,一旦为一条总线申请了一个struct i2c_type的数据结构之后,它就知道它的devices链表和drivers链表会包含哪些东西,这些咚咚一定不会是先天就有的,只能是后天填进来的.而具体到i2c系统,完成这个工作的就是i2c_core.i2c_core的代码会进行整个i2c系统的初始化,比如申请struct i2c_type i2c_bus_type,然后会扫描i2c总线,看线上连接了哪些i2c设备,那么就为它准备一个包含struct device的具体的设备结构体struct  i2c_device,根据它的实际情况,为这个struct  i2c_device赋值,并插入devices链表中来.最终就把i2c_bus_type中的devices链表给建立了起来.

那么drivers链表呢?这个就不用bus方面主动了,而该由每一个driver本身去bus上面登记,或者说挂牌.具体到i2c系统,每一个i2c设备的驱动程序都会有一个struct i2c_driver结构体(里面会包含一个struct device_driver),其代码如下,来自include/linux/i2c.h

I2c_core为每一个设备驱动准备了一个函数,让它把自己的这个struct i2c_driver 插入到i2c_bus_type中的drivers链表中去.而这个函数正是我们此前看到的i2c_add_driver

static int __init lsm303dlhc_acc_init(void)

{

         pr_info("%saccelerometer driver: init\n",

                                                        LSM303DLHC_ACC_DEV_NAME);

         returni2c_add_driver(&lsm303dlhc_acc_driver);

}

而与之对应的i2c_del_driver所从事的正是与之相反的工作,把这个结构体从drivers链表中删除.可以说i2c_core的确是用心良苦,为每一个i2c设备驱动做足了功课,正因为如此,作为一个实际的i2c设备驱动,它在初始化阶段所要做的事情就很少,很简单了,直接调用i2c_add_driver即可.事实上,没有人是理所当然应该为你做什么的,但i2c_core这么做了.所以每一个写i2c设备驱动的人应该铭记i2c_driver绝不是一个人在工作,在他身后,是i2c_core所提供的默默无闻又不可或缺的支持.

下面就看看具体到i2c设备,即我们的传感器设备,i2c_bus_type的devices_kset设备链表里的i2c_device和drivers_kset设备链表里的i2c_drivers是如何联系起来的呢此刻,必须抛出这样一个问题,先有device还是driver?

很久很久以前,在那激情燃烧的岁月里,先有的是device,每一个要用的device在计算机启动之前就已经插好了,插放在它应该在的位置上,然后计算机启动,然后操作系统开始初始化,总线开始扫描设备,每找到一个设备,就为其申请一个struct device结构,并且挂入总线中的devices链表中来,然后每一个驱动程序开始初始化,开始注册其struct device_driver结构,然后它去总线的devices链表中去寻找(遍历),去寻找每一个还没有绑定driver的设备,即struct device中的struct device_driver指针仍为空的设备,然后它会去观察这种设备的特征,看是否是他所支持的设备,如果是,那么调用一个叫做device_bind_driver的函数,然后他们就结为了秦晋之好.换句话说,把struct device中的struct device_driver driver指向这个driver,而struct device_driver driver把struct device加入他的那张struct list_head devices链表中来.就这样,bus,device,和driver,这三者之间或者说他们中的两两之间,就给联系上了.知道其中之一,就能找到另外两个

还有一种情况就是热插拔设备,需要自己去看

我们继续,理顺并完善了bus_type ,device,driver这三者之间的关系,也正是每一个设备驱动初始化阶段所完成的重要使命之一.让我们还是回到代码中来i2c_add_driver这个函数调用是调用了,但是传递给他的参数是什么呢?

static struct i2c_driverlsm303dlhc_acc_driver = {

         .driver= {

                            .owner= THIS_MODULE,

                            .name= LSM303DLHC_ACC_DEV_NAME,

                     },

         .probe= lsm303dlhc_acc_probe,

         .remove= lsm303dlhc_acc_remove,

         .suspend= lsm303dlhc_acc_suspend,

         .resume= lsm303dlhc_acc_resume,

         .id_table= lsm303dlhc_acc_id,

};

 

static int __init lsm303dlhc_acc_init(void)

{

         pr_info("%saccelerometer driver: init\n",

                                                        LSM303DLHC_ACC_DEV_NAME);

         returni2c_add_driver(&lsm303dlhc_acc_driver);

由上可知传递的是i2c_driver。

可以看到这里定义了一个struct i2c_driver的结构体变量, lsm303dlhc_acc_driver,关于lsm303dlhc_acc_driver我们上节已经说过了,当时主要说的是其中的成员driver,而眼下要讲的则是另外几个成员.首先,.owner和.name这两个没啥好多说的,owner这玩艺是用来给模块计数的,每个模块都这么用,赋值总是THIS_MODULE,而name就是这个模块的名字,i2c_core会处理它,所以如果这个模块正常被加载了的话,使用lsmod命令能看到一个叫做lsm303dlhc_acc的模块名.重点要讲一讲,.probe和.remove以及这个id_table.

probe,disconnect,id_table,这三个咚咚中首先要登场亮相的是id_table,它是干嘛用的呢?

我们说过,一个device只能绑定一个driver,但driver并非只能支持一种设备,也正是因为一个模块可以被多个设备共用,才会有模块计数这么一个说法.

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

具体看一下id_table

const struct i2c_device_id *id_table;即

struct i2c_device_id {

         charname[I2C_NAME_SIZE];

         kernel_ulong_tdriver_data;   /* Data private to thedriver */

};

实际上这个结构体对每一个i2c设备来说,就相当于是她的身份证,记录了她的一些基本信息,通常我们的身份证上会记录我们的姓名,性别,出生年月,户口地址等等,而i2c设备她也有她需要记录的信息,以区分她和别的i2c设备,比如Vendor-厂家,Product-产品,以及其他一些比如产品编号,产品的类别,遵循的协议,这些都会在i2c的规范里边找到对应的冬冬.暂且先不细说.

于是我们知道,一个i2c_driver会把它的这张id表去和每一个i2c设备的实际情况进行比较,如果该设备的实际情况和这张表里的某一个id相同,才能够把一个i2c device和这个i2c_driver进行绑定,这些特征哪怕差一点也不行.

那么i2c设备的实际情况是什么时候建立起来的?嗯,在介绍.probe指针之前有必要先谈一谈另一个数据结构了,她就是struct i2c_device.

在i2c_driver中.probe 和.remove的格式如下

    int (*probe)(struct i2c_client *, const struct i2c_device_id *);

    int (*remove)(struct i2c_client *);

看了到了没?probe的参数不是我们之前一直讲的i2c_device而是i2c_client(后面会讲到)

那么i2c_client是从何而来的呢?我们先从i2c_device说起吧

原型如下:

struct lsm303dlhc_acc_status {

    struct i2c_client *client;

    struct lsm303dlhc_acc_platform_data *pdata;

 

    struct mutex lock;

    struct delayed_work input_work;

 

    struct input_dev *input_dev;

 

    int hw_initialized;

    /* hw_working=-1 means not tested yet */

    int hw_working;

    atomic_t enabled;

    int on_before_suspend;

    int use_smbus;

 

    u8 sensitivity;

 

    u8 resume_state[RESUME_ENTRIES];

 

    int irq1;

    struct work_struct irq1_work;

    struct workqueue_struct *irq1_work_queue;

    int irq2;

    struct work_struct irq2_work;

    struct workqueue_struct *irq2_work_queue;

 

#ifdef DEBUG

    u8 reg_addr;

#endif

};

看红色字体的两个结构体,分别是i2c_client和input_dev,没错struct device 就是在input_dev中的

实际上,i2c设备驱动里边并不会直接去处理这个结构体,因为对于一个i2c设备来说,她就是对应这么一个struct lsm303dlhc_acc_status的变量,这个变量由i2c_core负责申请和赋值.但是我们需要记住这个结构体变量,因为日后我们调用i2c_core提供的函数的时候,会把这个变量作为参数传递上去,因为很简单,要和i2c_core交流,总得让人家知道我们是谁吧,比如后来要调用的一个函数,i2c_buffer_alloc,它就需要这个参数.

而对i2c设备驱动来说,比这个struct lsm303dlhc_acc_status更重要的数据结构是,struct i2c_client.走到这一步,我们不得不去了解一点i2c设备的规范了,或者专业一点说,i2c协议.因为我们至少要知道什么是i2c_client

对于i2c设备驱动程序编写者来说,更为关键的是下面一个,i2c_client,i2c_client.第一句,一个i2c_client对应一个i2c设备驱动程序.没错,还说前边那个例子,i2c设备,有加速传感器和光线传感器,那这样肯定得要两个驱动程序,一个是加速驱动程序,一个是光线驱动程序.我门用i2c_client来区分这两者.(为什么不用i2c_device来区别呢,又多出一个i2c_client,不是很理解,求大神指导)于是有了我们前面提到的那个数据结构,struct i2c_client.它定义于include/linux/i2c.h:

/**

 * struct i2c_client -represent an I2C slave device

 * @flags: I2C_CLIENT_TEN indicates the deviceuses a ten bit chip address;

 *  I2C_CLIENT_PECindicates it uses SMBus Packet Error Checking

 * @addr: Address used on the I2C bus connectedto the parent adapter.

 * @name: Indicates the type of the device,usually a chip name that's

 *  genericenough to hide second-sourcing and compatible revisions.

 * @adapter: manages the bus segment hostingthis I2C device

 * @driver: device's driver, hence pointer toaccess routines

 * @dev: Driver model device node for theslave.

 * @irq: indicates the IRQ generated by thisdevice (if any)

 * @detected: member of an i2c_driver.clientslist or i2c-core's

 *  userspace_deviceslist

 *

 * An i2c_client identifies a single device(i.e. chip) connected to an

 * i2c bus. The behaviour exposed to Linux isdefined 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 i2c_driver *driver;  /*and our access routines  */

    struct device dev;       /* the device structure     */

    int irq;          /* irqissued by device     */

    struct list_head detected;

};

看到了没上面的第一个注释i2c_client代表一个i2c从设备,清楚了吧。不知道为什么不要紧,先知道是干什么了就可以了

所以虽然我们不用去深入了解,但是需要记住,整个i2c驱动程序在后面任何一处提到的struct i2c_client都是同一个变量,这个变量是在i2c core总线扫描的时候就申请好了的.我们只需贯彻鲁迅先生的拿来主义即可,直接用就是了.比如前面说过的storage_probe(structusb_interface *intf,const struct usb_device_id *id),storage_disconnect(structusb_interface *intf)这两个函数中的那个参数intf.

 还有就是lsm303dlhc_acc_status(即具体的i2c_client)和i2c_client如何转换的。是通过系统函数container_of()进行转换的,转化之前需要先进行设置,

stat->client =client;

i2c_set_clientdata(client,stat);stat就是lsm303dlhc_acc_status

到处整个驱动之前的工作就全搞定了,下面就是probe函数了

对于整个i2c模块, lsm303dlhc_acc_init是它的开始,然而,对于i2c驱动程序来说,它真正驱使U盘工作却是始于lsm303dlhc_acc_probe().

两条平行线只要相交,就注定开始纠缠一生,不管中间是否短暂分离. I2c_core为设备找到了适合她的驱动程序,或者为驱动程序找到了他所支持的设备,但这只是表明双方的第一印象还可以,但彼此是否真的适合对方还需要进一步的了解.毋庸置疑,了解对方的第一步是,知道她有哪些爱好,她的生日,她的星座,喜欢吃什么,而加速传感器driver则会调用函数lsm303dlhc_acc_probe()去认识对方,她是个什么样的设备,它的功能,他的工作方式?

接下来就去探索probe函数去吧(本篇就不探索了,以后会写的)

 

 

 

 

0 0
原创粉丝点击