Chinaunix首页 | 论坛 | 博客
  • 博客访问: 353291
  • 博文数量: 127
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 333
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-27 14:44
个人简介

兴趣是最好的学习契机!

文章分类

全部博文(127)

文章存档

2017年(1)

2016年(3)

2015年(54)

2014年(58)

2013年(11)

我的朋友

分类: LINUX

2013-08-22 15:13:06

 

Linux中总线、设备、驱动是如何关联的?

作者:武汉华嵌技术部

 

对于Linux驱动开发来说,设备模型的理解是根本,顾名思义设备模型是关于设备的模型,设备的概念就是总线和与其相连的各种设备了。

电脑城的IT 工作者都会知道设备是通过总线连到计算机上的,而且还需要对应的驱动才能用,可是总线是如何发现设备的,设备又是如何和驱动对应起来的?

总线、设备、驱动,也就是busdevicedriver,在内核里都会有它们自己专属的结构,在include/linux/device.h 里定义。

首先是总线,bus_type.
struct bus_type {
const char  * name;
struct subsystem subsys;//
代表自身
struct kset  drivers;   //当前总线的设备驱动集合
struct kset  devices; //所有设备集合
struct klist  klist_devices;
struct klist  klist_drivers;
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, char **envp,   
      int num_envp, char *buffer, int buffer_size);//
热拔插事件
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);
};

下面是设备device的定义:

struct device {
struct device  * parent; //
父设备,一般一个bus也对应一个设备。
struct kobject kobj;//代表自身
char bus_id[BUS_ID_SIZE];
struct bus_type * bus;  /*
所属的总线 */
struct device_driver *driver; /*
匹配的驱动*/
void  *driver_data; /* data private to the driver
指向驱动 */
void  *platform_data; /* Platform specific data
,由驱动定义并使用*/
///
更多字段忽略了
};
下面是设备驱动定义:
struct device_driver {
const char  * name;
struct bus_type  * bus;//
所属总线
struct completion unloaded;
struct kobject  kobj;//
代表自身
struct klist  klist_devices;//设备列表
struct klist_node knode_bus;
struct module  * owner;
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);
};

 

我们会发现,structbus_type中有成员structksetdrivers structksetdevices,同时structdevice中有两个成员struct bus_type * busstruct device_driver *driver structdevice_driver中有两个成员structbus_type*busstructklistklist_devicesstructdevice中的bus表示这个设备连到哪个总线上,driver表示这个设备的驱动是什么,structdevice_driver中的bus表示这个驱动属于哪个总线,klist_devices表示这个驱动都支持哪些设备,因为这里device是复数,又是list,更因为一个驱动可以支持多个设备,而一个设备只能绑定一个驱动。当然,structbus_type中的driversdevices分别表示了这个总线拥有哪些设备和哪些驱动。

还有上面device driver结构里出现的kobject 结构是什么?kobject kset 都是Linux 设备模型中最基本的元素。一般来说应该这么理解,整个Linux 的设备模型是一个OO 的体系结构,总线、设备和驱动都是其中鲜活存在的对象,kobject 是它们的基类,所实现的只是一些公共的接口,kset 是同种类型kobject 对象的集合,也可以说是对象的容器。

那么总线、设备和驱动之间是如何关联的呢?

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

设备和驱动又是如何联系?

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

但现在情况变了,出现了一种新的名词,叫热插拔。设备可以在计算机启动以后在插入或者拔出计算机了。设备可以在任何时刻出现,而驱动也可以在任何时刻被加载,所以,出现的情况就是,每当一个structdevice 诞生,它就会去bus drivers链表中寻找自己的另一半,反之,每当一个struct device_driver 诞生,它就去busdevices 链表中寻找它的那些设备。如果找到了合适的,那么OK,和之前那种情况一下,调device_bind_driver 绑定好。如果找不到,没有关系,等待吧!

+----> drivers     probe()

驱动挂接到总线上时,与总线上的所有设备进行匹配(bus_type.match进行匹配)

如果匹配成功,则调用bus_type.probe或者driver.probe初始化该设备;挂接到总线上

如果匹配失败,则只是将该驱动挂接到总线上。

需要重点关注的是总线的匹配函数match(),驱动的初始化函数probe()

1. platform_bus_type--总线先被kenrel注册。

2. 系统初始化过程中调用platform_add_devices或者platform_device_register,将平台设备(platform devices)注册到平台总线中(platform_bus_type)

3. 平台驱动(platform driver)与平台设备(platform device)的关联是在platform_driver_register或者driver_register中实现,一般这个函数在驱动的初始化过程调用。

通过这三步,就将平台总线,设备,驱动关联起来。

1. platform bus先被kenrel注册。

------------------------------------------------------

do_basic_setup() -->-driver_init() -->-platform_bus_init()-->bus_register()

2. 系统初始化过程中调用platform_add_devices或者platform_device_register,将平台设备(platform devices)注册到平台总线中(platform_bus_type)

------------------------------------------------------

系统启动阶段,总线的驱动链表还是空的,所以启动阶段的platform_add_devices()只负责将设备添加到总线的设备链表上。

linux-2.6.26/drivers/base/platform.c

int platform_add_devices(struct platform_device **devs, int num)

{

----...

----ret = platform_device_register(devs[i]);

----...

}

int platform_device_register(struct platform_device *pdev)

{

----device_initialize(&pdev->dev);

----return platform_device_add(pdev);

}

int platform_device_add(struct platform_device *pdev)

{

----...

----pdev->dev.bus = &platform_bus_type;

----...

----ret = device_add(&pdev->dev);

----...

}

device_add() -->-bus_attach_device()

void bus_attach_device(struct device *dev)

{

----struct bus_type *bus = dev->bus;

----int ret = 0;

----if (bus) {

--------if (bus->p->drivers_autoprobe)

------------ret = device_attach(dev);

--------WARN_ON(ret < 0);

--------if (ret >= 0)

------------klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);

----}

}

如果设备和驱动匹配成功;或者设备已经注册,但是总线上没有与之相匹配的驱动,bus_attach_device()将调用klist_add_tail()将设备添加到总线的设备链表尾部。

本篇文章来源于 Linux公社网站()  原文链接:http:///Linux/2011-05/36302.htm

 

阅读(529) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~