一、定义:
linux/include/linux/device.h

407struct device {
408 struct klist klist_children;
409 struct klist_node knode_parent
410 struct klist_node knode_driver;
411 struct klist_node knode_bus;
412 struct device *parent;
413
414 struct kobject kobj;
415 char bus_id[BUS_ID_SIZE]; 
416 struct device_type *type;
417 unsigned is_registered:1;
418 unsigned uevent_suppress:1;
419
420 struct semaphore sem
423
424 struct bus_type * bus
425 struct device_driver *driver
427 void *driver_data
428 void *platform_data
430 struct dev_pm_info power;
431
432#ifdef CONFIG_NUMA
433 int numa_node
434#endif
435 u64 *dma_mask
436 u64 coherent_dma_mask;
441
442 struct list_head dma_pools
443
444 struct dma_coherent_mem *dma_mem
446 
447 struct dev_archdata archdata;
448
449 spinlock_t devres_lock;
450 struct list_head devres_head;
451
452 
453 struct list_head node;
454 struct class *class;
455 dev_t devt
456 struct attribute_group **groups
457
458 void (*release)(struct device * dev);
459};

二、作用:
用于描述设备相关的信息设备之间的层次关系,以及设备与总线、驱动的关系。
三、详解:
1、struct klist            klist_children;
struct klist被定义在linux/include/linux/klist.h中,原型是:
21struct klist {
22 spinlock_t k_lock;
23 struct list_head k_list;
24 void (*get)(struct klist_node *);
25 void (*put)(struct klist_node *);
26};

可见它是对struct list_head的扩展,在此它的作用是连接设备列表中的孩子列表。
2、struct klist_node       knode_parent;
struct klist_node被定义在linux/include/linux/klist.h,原型是:
32struct klist_node {
33 struct klist * n_klist;
34 struct list_head n_node;
35 struct kref n_ref;
36 struct completion n_removed;
37};

在此它的作用是表示它的兄弟节点。
3、struct klist_node       knode_driver;
表示它的驱动节点。
4、struct klist_node       knode_bus;
表示总线节点。
5、struct device           *parent;
指向其父设备。
6、struct kobject kobj;
这里http://blog.chinaunix.net/u1/55599/showart.php?id=1086478
有对kobject的解释,此处它是内嵌的一个kobject对象。
7、char    bus_id[BUS_ID_SIZE]; 
bus_id表示其在父总线上的位置。BUS_ID_SIZE被定义为:
#define KOBJ_NAME_LEN 20
#define BUS_ID_SIZE KOBJ_NAME_LEN 

所以表示位置的字符串长度不能超过20。
8、struct device_type      *type;
被定义在/linux/include/linux/device.h中,原型是:
337struct device_type {
338 const char *name;
339 struct attribute_group **groups;
340 int (*uevent)(struct device *dev, char **envp, int num_envp,
341 char *buffer, int buffer_size);
342 void (*release)(struct device *dev);
343 int (*suspend)(struct device * devpm_message_t state);
344 int (*resume)(struct device * dev);
345};

device_type结构表示设备的类型。一个设备类或者总线可以包含不同类型的设备,例如“分区”和“磁盘” , “鼠标”和“事件” 。device_type就可以标识某个设备类型和该设备的特有信息,它就等同于kobject结构中的kobj_type一样。如果name数据成员被指定,那么uevent成员函数就会把它包含在DEVTYPE变量中。

9、unsigned is_registered:1;
标识该设备是否已经被注册过。is_registered:1这样的形式表示is_registered这个变量只有一

位。在32位linux系统下,unsigned是4字节32位,而经过is_registered:1这样的限制后,变量

is_registered只有一位,其取值只能是1或者0,相当于声明了一个boolean类型的变量。在此种

用法中,后面指定数据宽度的值只能大于0且小于本身的数据宽度。
10、struct bus_type * bus;
指向所连接总线的指针。
11、struct device_driver *driver; 
指向被分配到该设备的设备驱动。
12、u64 *dma_mask;    /*指向设备DMA屏蔽字。*/
u64 coherent_dma_mask;/*设备一致性DMA的屏蔽字。*/
struct list_head dma_pools; /*聚集的DMA缓冲池。*/
struct dma_coherent_mem *dma_mem; /*指向设备所使用的一致性DMA存储器描述符的指针*/
13、spinlock_t devres_lock;
定义一个设备自旋锁,用于互斥访问设备。关于自旋锁的详细讲解参考:
http://www.deansys.com/doc/ldd3/ch05s05.html
14、void    (*release)(struct device * dev);
释放设备描述符的回调函数。
四、操作:
linux内核系统了一系列完整的对device操作的函数。
1、其中device_register()函数用来将一个新的device对象插入设备模型。它在

linux/drivers/base/core.c中被实现:
int device_register(struct device *dev){        device_initialize(dev);       

return device_add(dev);}

该函数首先是调用device_initialize()初始化device结构,具体是初始化嵌入的kobject结构

dev->kobj,初始化列表中的孩子列表kobj->klist_children,初始化DMA缓冲池dev->dma_pools,

初始化自旋锁dev->devres_lock等。接下来device_add()函数才真正将该device对象dev插入设

备模型中。device_add()函数首先是通过kboject_add()函数将它添加到kobject层次,再把它添

加都全局和兄弟链表中,最后添加到其他相关的子系统的驱动程序模型,完成device对象的注册

2、device_unregister()完成相反的过程:/linux/drivers/base/core.c
void device_unregister(struct device * dev){        
pr_debug("DEV: Unregistering device. ID = '%s'\n", dev->bus_id);        
device_del(dev);        
put_device(dev);
}

它会先以KERN_DEBUG级别打印注销设备的信息,然后才真正删除设备,减少设备对象的引用计数


3、get_device()和put_device()分别是增加和减少设备对象的引用计数。这两个函数都定义在

:/linux/drivers/base/core.c中。具体是应用在注册device对象时,device_add()函数会调用

get_device()增加对该device对象的引用计数。在注销设备对象时,device_unregister()函数

直接调用put_device()函数减少对该device对象的引用计数。



device_driver

系统的每一个驱动程序都由struct device_driver,定义在/include/linux/device.h:
struct device_driver {
        const char              * name; 

 /*设备驱动程序的名称。在调用driver_register()往设备驱动程序模型中插入一个新的device_driver对象时,driver_register()函数会调用bus_add_driver()函数,bus_add_driver()调用kobject_set_name()函数将name赋给drv>kobj.name或者drv->kobj.k_name。注:drv为要调用driver_register()注册的device_driver类型的对象。*/
        struct bus_type         * bus;   //指向总线描述符的指针,总线连接所支持的设备
        struct kobject          kobj;     //内嵌的kobject结构,主要用于计数
        struct klist            klist_devices;  
        struct klist_node       knode_bus;
        struct module           * owner;
        const char              * mod_name;     
        struct module_kobject   * mkobj;
        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);   //将挂起的驱动恢复运行
};

调用函数:

1、driver_register()函数:/linux/drivers/base/driver.c 
int driver_register(struct device_driver * drv)
 {if ((drv->bus->probe && drv->probe) ||(drv->bus->remove && drv->remove) ||(drv->bus->shutdown && drv->shutdown)) 
  {printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods/n", drv->name);}
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);} 
函数首先是检查device_driver类型的drv对象和其bus域的回调函数是否已经被赋值,如果为空,则打印警告。核心的注册是调用bus_add_driver()函数实现。在bus_add_driver()函数中,首先是检查drv->bus,如果为空可立即返回EINVAL(无效的变量),注册失败,可见bus域必须提前初始化好才行。接下来是对kobj域进行初始化,检查bus域等。最后调用add_bind_files()函数(实际上是由add_bind_files()函数调用driver_create_file()函数)在sys文件系统下为其创建一个新的目录。

 

2、driver_unregister()函数:/linux/drivers/base/driver.c/linux/drivers/base/driver.c
void driver_unregister(struct device_driver * drv)
{
        bus_remove_driver(drv);
}
调用bus_remove_driver在/linux/drivers/base/bus.c:
void bus_remove_driver(struct device_driver * drv)
{
        if (!drv->bus)
                return;

        remove_bind_files(drv);
        driver_remove_attrs(drv->bus, drv);
        klist_remove(&drv->knode_bus);
        pr_debug("bus %s: remove driver %s/n", drv->bus->name, drv->name);
        driver_detach(drv);
        module_remove_driver(drv);
        kobject_unregister(&drv->kobj);
        put_bus(drv->bus);
}

 

在bus_remove_driver()函数中首先是检查要卸载的device_driver类型的对象drv的bus域,如果为空则返回。此种情况会发生在调用driver_register()函数注册drv时没有检查返回值,注册失败,但程序依然向下运行,到driver_unregister()时就会到688行处返回。接下来会删除在调用driver_register()函数注册时在sys文件系统中创建的目录,然后删除drv对象的属性,(属性是记录在文件中的,删除属性其实是删除记录drv->bus属性的文件),删除驱动模块,减少对drv->bus的引用计数等。

 

3、get_driver()和put_driver()函数:分别是增加和减少对该device_drvier类型的对象的引用计数。

 

4、int driver_create_file(struct device_driver * drv, struct driver_attribute * attr); 

    void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);

分别是在sys文件系统中为device_driver对象创建和删除目录

******************************************************************************************************
对于Linux驱动开发来说,设备模型的理解是根本,顾名思义设备模型是关于设备的模型,设备的概念就是总线和与其相连的各种设备了。 
设备是通过总线连到计算机上的,需要对应的驱动才能用,可是总线是如何发现设备的,设备又是如何和驱动对应起来的? 
总线、设备、驱动,也就是bus、device、driver,在内核里都会有它们自己专属的结构,在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 * bus和struct device_driver *driver , structdevice_driver中有两个成员structbus_type*bus和structklistklist_devices。structdevice中的bus表示这个设备连到哪个总线上,driver表示这个设备的驱动是什么,structdevice_driver中的bus表示这个驱动属于哪个总线,klist_devices表示这个驱动都支持哪些设备,因为这里device是复数,又是list,更因为一个驱动可以支持多个设备,而一个设备只能绑定一个驱动。当然,structbus_type中的drivers和devices分别表示了这个总线拥有哪些设备和哪些驱动。
还有上面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链表中来。就这样,bus、device 和driver,这三者之间或者说他们中的两两之间,就给联系上了。知道其中之一,就能找到另外两个
Logo

更多推荐