专题2-总线设备驱动模型

一.总线模型
1.随着技术的进步,对热插拔的要求以及可移植性的要求越来越高,从Linux2.4开始虽然有了模型但是正式提出是在Linux2.6。
2.关键词是总线,驱动,设备
3.总线能够感知设备的插拔:
(1)插入新设备的时候知道有设备插入,那么就去总线上已有的驱动里面查找能够处理该新设备的驱动,一旦匹配,该驱动就有了该设备的控制权
(2)拔出的时候,总线也能感知,并且告诉相应的驱动从而使得驱动能够处理拔出事件
二.总线的描述
1.在linux/device.h里面定义的bus_type结构体类型。
2.struct bus_type {
     const char          *name;
     struct bus_attribute     *bus_attrs;
     struct device_attribute     *dev_attrs;
     struct driver_attribute     *drv_attrs;
     int (*match)(struct device *dev, struct device_driver *drv);
     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
     int (*probe)(struct device *dev);
     int (*remove)(struct device *dev);
     void (*shutdown)(struct device *dev);
     int (*suspend)(struct device *dev, pm_message_t state);
     int (*resume)(struct device *dev);
     const struct dev_pm_ops *pm;
     struct subsys_private *p;
};
3.最主要的是
(1)const char          *name;描述的是总线名称
(2)  int (*match)(struct device *dev, struct device_driver *drv);完成驱动和设备的匹配功能。判断指定的驱动程序能否处理指定的设备,能就返回非0值。
4.注册总线
int bus_register(struct bus_type *bus);
5.注销
void bus_unregister(struct bus_type *bus);
6.代码

三.驱动程序
1.描述结构
struct device_driver {
     const char          *name;
     struct bus_type          *bus;
     struct module          *owner;
     const char          *mod_name;     /* used for built-in modules */
     bool suppress_bind_attrs;     /* disables bind/unbind via sysfs */
     const struct of_device_id     *of_match_table;
     int (*probe) (struct device *dev);
     int (*remove) (struct device *dev);
     void (*shutdown) (struct device *dev);
     int (*suspend) (struct device *dev, pm_message_t state);
     int (*resume) (struct device *dev);
     const struct attribute_group **groups;
     const struct dev_pm_ops *pm;
     struct driver_private *p;
};
2.最主要初始化的是
(1) const char          *name;描述驱动名称
(2) struct bus_type          *bus;描述该驱动要挂到什么总线
(3) int (*probe) (struct device *dev);当总线对驱动和设备匹配成功的时候,总线会调用该函数对相应的设备进行一些初始化等设备相关的操作。
3.注册
int driver_register(struct device_driver *drv)
4.注销
void driver_unregister(struct device_driver *drv)
5.代码

四.设备
1.描述结构
struct device {
     struct device          *parent;
     struct device_private     *p;
     struct kobject kobj;
     const char          *init_name; /* initial name of the device */
     const struct device_type *type;
     struct mutex          mutex;     /* mutex to synchronize calls to
                         * its driver.
                         */
     struct bus_type     *bus;          /* type of bus device is on */
     struct device_driver *driver;     /* which driver has allocated this
                            device */
     void          *platform_data;     /* Platform specific data, device
                            core doesn't touch it */
     struct dev_pm_info     power;
     struct dev_power_domain     *pwr_domain;
#ifdef CONFIG_NUMA
     int          numa_node;     /* NUMA node this device is close to */
#endif
     u64          *dma_mask;     /* dma mask (if dma'able device) */
     u64          coherent_dma_mask;/* Like dma_mask, but for
                              alloc_coherent mappings as
                              not all hardware supports
                              64 bit addresses for consistent
                              allocations such descriptors. */
     struct device_dma_parameters *dma_parms;
     struct list_head     dma_pools;     /* dma pools (if dma'ble) */
     struct dma_coherent_mem     *dma_mem; /* internal for coherent mem
                              override */
     /* arch specific additions */
     struct dev_archdata     archdata;
     struct device_node     *of_node; /* associated device tree node */
     dev_t               devt;     /* dev_t, creates the sysfs "dev" */
     spinlock_t          devres_lock;
     struct list_head     devres_head;
     struct klist_node     knode_class;
     struct class          *class;
     const struct attribute_group **groups;     /* optional groups */
     void     (*release)(struct device *dev);
};
2.最重要的是
(1) const char          *init_name; /* initial name of the device */即表示设备的名字
(2)struct bus_type     *bus;          /* type of bus device is on */设备所在的总线
3.注册
int device_register(struct device *dev)
4.注销
void device_unregister(struct device *dev)
5.代码

五.不同总线匹配设备和驱动的方式不一样。
通常是匹配驱动和设备的名字来决定match函数的返回值,从而决定是否匹配成功以及是否能够调用probe函数。
六.内核异常之后通常需要重启
七.根据内核源代码,发现在device注册的时候,如果init_name已经存在,则将其置为空指针,并复制到device.kobj.name,所以当我们匹配的时候如果直接使用init_name,则会出现内核级别的段错误。
if (dev->init_name) {
          dev_set_name(dev, "%s", dev->init_name);
          dev->init_name = NULL;
     }
。。。。。。
int dev_set_name(struct device *dev, const char *fmt, ...)
{
     va_list vargs;
     int err;
     va_start(vargs, fmt);
     err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
     va_end(vargs);
     return err;
}
。。。。。。。。
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
                      va_list vargs)
{
     const char *old_name = kobj->name;
     char *s;
     if (kobj->name && !fmt)
          return 0;
     kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
     if (!kobj->name)
          return -ENOMEM;
     /* ewww... some of these buggers have '/' in the name ... */
     while ((s = strchr(kobj->name, '/')))
          s[0] = '!';
     kfree(old_name);
     return 0;
}
可以看到
  kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
就是把init_name复制到device.kobj.name。所以匹配的时候要比较kobj.name和drv.name。
八.不管先有驱动还是先有设备,都是同样的匹配模式。从而支持热插拔很好,而且移植性更高。因为只需要改动驱动和设备要挂载到的总线类型即可。在device和device_driver结构体变量初始化的地方修改。

九.平台总线驱动模型
1.Linux从2.6以后就引入了平台总线的概念。
2.平台总线是一种虚拟总线,Linux不仅可以支持实际总线,如USB,PCI总线等。也可以支持一些虚拟的总线。
3.这些虚拟总线和实际总线拥有完全相同的特性,包括可移植性以及热插拔检测等。
十.总线描述结构
struct bus_type platform_bus_type = {
     .name          = "platform",
     .dev_attrs     = platform_dev_attrs,
     .match          = platform_match,
     .uevent          = platform_uevent,
     .pm          = &platform_dev_pm_ops,
};
(1)平台总线名称:platform_bus_type
(2)实现文件/drivers/base/platform.c
十一.匹配设备和驱动的函数
由上面描述结构可知是 .match          = platform_match,即
static int platform_match(struct device *dev, struct device_driver *drv)
{
     struct platform_device *pdev = to_platform_device(dev);
     struct platform_driver *pdrv = to_platform_driver(drv);
     /* Attempt an OF style match first */
     if (of_driver_match_device(dev, drv))
          return 1;
     /* Then try to match against the id table */
     if (pdrv->id_table)
          return platform_match_id(pdrv->id_table, pdev) != NULL;
     /* fall-back to driver name match */
     return (strcmp(pdev->name, drv->name) == 0);
}
可见在Linux3.0x版本的内核中有三种匹配方式。
1./*通过驱动里定义了of_device_id项,则通过这一项来比对;*
2./*如果在平台驱动中定义了id_table项,则通过对比id_table来判断,即芯片ID*/
3./*通过对比平台设备名字和平台驱动名字来判断*/
如果driver中定义了of_device_id,则通过driver中的of_device_id和device中的device_node内容进行匹配判断,匹配工作由of_match_node来完成,该函数会遍历of_device_id列表,查找是否有成员与device_node相匹配,具体由matches的name,type和compatioble来进行对比,如果找到则返回相应的表项,否则返回null.如果没有定义of_device_id,device_node或不能找到对应的匹配项,则通过第二种方式platform_device_id来进行对比匹配,通过platform_match_id来完成。platform_match_id函数遍历platfrom_device_id列表,通过比对平台设备与id的name来确定是否有匹配项,如果找到匹配的,则返回对应的id项,否则返回null。如果没有定义platform_device_id或没有找到匹配项,则通过第三种方式进行匹配,第三种方式通过比对平台设备和平台驱动的名字,如果相等,则匹配成功,否则失败。
十二.平台设备
1.描述结构(在platform_device.h中定义)
struct platform_device {
     const char     * name;
     int          id;
     struct device     dev;
     u32          num_resources;
     struct resource     * resource;
     const struct platform_device_id     *id_entry;
     /* MFD cell pointer */
     struct mfd_cell *mfd_cell;
     /* arch specific additions */
     struct pdev_archdata     archdata;
};
2.最重要的:
(1)const char     * name;定义设备名称
(2) int          id;设备编号,配合名称使用
(3) struct resource     * resource;设备拥有的资源
(4)  u32          num_resources;拥有的资源数目,即resource成员代表的数组的元素个数。
具体结构如下
struct resource {
     resource_size_t start;
     resource_size_t end;
     const char *name;
     unsigned long flags;
     struct resource *parent, *sibling, *child;
};
主要的是
--》 resource_size_t start;起始值
--》 resource_size_t end;终止值
--》unsigned long flags;资源类型,包括中断号,内存(寄存器基地址)等。
(5)一般初始化设备指定资源的时候都要事先初始化一个资源数组。
3.注册设备:(挂载到平台总线)
int platform_device_register(struct platform_device *pdev);
4.注销
int platform_device_unregister(struct platform_device *pdev);
5.代码
主要是平台设备资源数组定义格式要注意,脑补C语言基础知识

十三.平台驱动
1.描述结构
struct platform_driver {
     int (*probe)(struct platform_device *);
     int (*remove)(struct platform_device *);
     void (*shutdown)(struct platform_device *);
     int (*suspend)(struct platform_device *, pm_message_t state);
     int (*resume)(struct platform_device *);
     struct device_driver driver;
     const struct platform_device_id *id_table;
};
2.主要初始化的
(1) int (*probe)(struct platform_device *);表示当驱动和设备匹配的时候调用的函数,一般是硬件相关的初始化
(2) int (*remove)(struct platform_device *);表示当对应的设备从平台总线移除的时候,调用的函数。
(3) const struct platform_device_id *id_table;用来匹配设备用的。
(4) struct device_driver driver;它里面的name成员能够指定设备驱动的名字。(3)和(4)初始化一个就可以了,当然可以同时初始化。
3.注册
int platform_driver_register(struct platform_driver *);
4.注销
int platform_driver_unregister(struct platform_driver *)
5.硬件相关的初始化一般放在probe指定的函数
6.平台总线只是负责匹配设备和驱动,当找到以后,我们还需要在probe里面进行硬件相关初始化以及其他必须的工作,比如注册设备,由此可见,一个设备如果在总线上,那么他必然是一个对应的平台设备。但是,与此同时,这个设备有可能是字符设备,网卡,USB设备,所以还需要我们继续完成其他设备类型的驱动工作。比如混杂设备,网络设备等的驱动步骤。故一般情况下平台驱动的注册在平台驱动文件的注册模块,而具体的这个硬件对应的另外类型的驱动的注册,(如混杂设备类型驱动的注册)则是放在probe函数,同样的这个平台驱动的卸载也是在平台驱动文件的卸载模块,而硬件其它类型驱动的卸载也是在remove指定的函数。一般情况是总线(驱动)包裹其他类型驱动的模型。
7.在平台总线驱动中,不适合出现硬件相关太多的东西,比如寄存器地址,比如中断号,这些已经作为平台设备的一种资源在平台设备里进行了初始化,我们要做的是如何提取平台设备里的资源信息。的那个总线匹配了设备和驱动的时候,会将设备的所有信息传递给probe指定的函数,我们就可以在该函数里面调用platform_get_resource(设备名,资源类型,该类型的资源序号)来获取相应的硬件信息。返回的是一个资源结构体指针,通过访问他的start,end成员就能得到很多硬件信息了。
8.代码
(1)先要定义获取硬件资源的变量,将platform_get_resource的返回值作为它们的值

其中key_base是按键的起始地址,是已经经过地址映射得到的结果,是虚拟地址。
(2)最核心的硬件初始化程序利用上述得到的值进行

(3)获取设备(硬件)资源以及完成其他层次驱动的注册

9.不一定要把设备单独写成模块,很多时候直接将设备加到内核初始化部分。因为实际上平台设备也就是一个变量,后期开发相应的驱动程序即可。

posted @ 2015-04-15 16:19  生活需要深度  阅读(150)  评论(0)    收藏  举报