linux内核之设备驱动模型
前言
一、设备模型的基石---kobject子系统
二、设备模型的总框架---总线、设备和驱动模型
三、有关驱动的虚拟内存文件系统---Sysfs系统
前言
七七八八,磨磨蹭蹭,终于大体上是把LDD3弄完了(大的方面),这里还剩一部分设备驱动部分,喝杯枸杞茶,拍拍我这略显懒散的脸,把这部分记录完。
这篇博客打算写一下几个部分
设备模型的基石—kobject子系统
设备模型的总框架—总线、设备和驱动模型
有关驱动的虚拟内存文件系统—Sysfs系统
ok,let’s begin…
一、设备模型的基石—kobject子系统
在面向对象的大型框架或者软件系统中,为了代码的复用性,大多数系统都会创造一个Object对象,以这个对象作为其他对象的祖先。作为开天辟地的老大,它一般对软件系统中所需要的对象做出了最高的抽象。 对于它的子孙们,大都需要继承Object,从而淋漓极致的发挥面向对象中继承和多态的功效。
用C语言开发的linux在设备驱动这个模块中也或多或少的用到了这中做法。
kobject是组成设备模型的基本结构,而内核中很多平台、总线或者框架的实现大都以kobject为基础(比如说后面说到的总线、设备和驱动模型)。由于linux使用C语言完成的,面向对象的那种纯纯的继承关系是体现不出来的,所以kobject的子孙们“继承”这个祖先的方式是包含一个object的结构体。如设备驱动中的字符设备cdev,其成员如下。
struct cdev {
struct kobject kobj; //内嵌的内核对象.
struct module *owner; //该字符设备所在的内核模块的对象指针.
const struct file_operations *ops; //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
struct list_head list; //用来将已经向内核注册的所有字符设备形成链表.
dev_t dev; //字符设备的设备号,由主设备号和次设备号构成.
unsigned int count; //隶属于同一主设备号的次设备号的个数.
这里再简单多说一点,以上所说的这种"继承"方式,有的C++教材中叫做组合的方式。同时,利用这种内嵌的组合方式,linux在实现多态的时候用到了一个叫做 container of()的方式(用结构体成员找结构体),感兴趣的可以问问度娘。
咱再回来接着说kobject,在2.6的内核中,它的基本结构为:
struct kobject {
const char*name;
struct list_headentry;
struct kobject*parent;
struct kset*kset;
struct kobj_type*ktype;
struct sysfs_dirent*sd;
struct krefkref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
LDD3中,Kobject的作用有以下几点:
(1)、对象的引用计数。结构体中kref主要实现的就是这个作用,控制对象的生命周期,即当内核中没有代码持有对象的引用时,对象就可以删除了。
(2)、数据结构的相互关联。object把大量的数据结构联系成了层次化的体系结构。(有点类似于对象的子类父类家族树)
(3)、sysfs表述。在sysfs中显示的每一个对象,都对应这一个kobject,它被用来于内核交互。
(4)、热插拔事件处理。当系统中的硬件被热插拔时,在kobject子系统的控制下,将产生事件通知用户空间。(本文不涉及这点)
由kobject所引申出来的几个结构体还有kset、kobj_type等等,它们的作用和object差不多(或者说它们是和kobject一起配合组成以上说明的作用)。网上有很多博客说的很好,目前我也想不出什么好的创新点,在此就不赘述了。给推荐一个博客。
linux设备驱动模型
二、设备模型的总框架—总线、设备和驱动模型
在linux驱动中应该有很多框架,但最主要的应该要数这个总线、设备和驱动模型框架了。
来,先来弄张图。
为了应对日益复杂的硬件设备,原先的linux内核有点吃不消,在全世界热爱linux的热血青年的共同努力下,应该在2.6版本之后就出现了上图所示的总线、设备驱动模型。
在这个模型中,有三个概念:
(1)、总线(Bus)。这里所说的总线并不是真正CPU内部的硬件总线,而是一个虚拟的概念。(之所以叫做总线,我想是因为设备、和驱动能“挂”在上面吧),从某种程度上来说,它是这个模型的控制者、总览大局的。模型中的设备和驱动需要挂在它下面才能使用。在linux内核中用bus_type来描述一个总线。
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 bus_type_private *p;
};
(2)设备(device)。在linux内核中用device结构体来描述。如下所示。
struct device {
struct device*parent;
struct device_private*p;
struct kobject kobj;
const char*init_name; /* initial name of the device */
struct device_type*type;
struct mutexmutex;/* 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_infopower;
#ifdef CONFIG_NUMA
intnuma_node;/* NUMA node this device is close to */
#endif
u64*dma_mask;/* dma mask (if dma'able device) */
u64coherent_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_headdma_pools;/* dma pools (if dma'ble) */
struct dma_coherent_mem*dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdataarchdata;
#ifdef CONFIG_OF
struct device_node*of_node;
#endif
dev_tdevt;/* dev_t, creates the sysfs "dev" */
spinlock_tdevres_lock;
struct list_headdevres_head;
struct klist_nodeknode_class;
struct class*class;
const struct attribute_group **groups;/* optional groups */
void(*release)(struct device *dev);
};
(3)驱动(driver)。在linux内核中用device_driver来描述。如下所示。
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 */
#if defined(CONFIG_OF)
const struct of_device_id*of_match_table;
#endif
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;
};
linux内核把设备驱动分开了,一个管设备,一个管驱动。它们在使用的时候都需要“挂”到对应的bus总线上(bus_type里有对应的kset,device和device_driver中也有指明哪条bus的bus_type类型), 它们就像一对小情侣,而bus就相当于红娘。这对小情侣的配对都需要红娘来牵线,当一个device来到红娘bus注册的时候,bus红娘就从已注册的device_driver中遍历,找到能和device匹配的(用bus_type里的match函数)。当device_driver来注册时也是一样。 需要说明一点的是,一般来说,一个设备只能匹配一个driver,而一个driver能够匹配多个device。(这么说的话,这里的小情侣应该是古时候的小情侣,嘿嘿)
这里我基本只说一些大的方面,具体的细节方面,有很多博客、资料说的已经很好了。这里也给一些资料。
1、从需求的角度去理解Linux系列:总线、设备和驱动
2、Linux设备驱动中的软件架构思想
3、linux设备驱动模型
4、linux驱动中probe函数是怎么调用的
这里在多说一点,这个总线(Bus)、设备(device)、驱动(device_driver)几个结构体也算是比较底层的结构体了,linux内核中比较成熟的总线都在这底层的结构体上进行了相应的封装(成员变量和对应的操作方法上)。如bus总线、pci总线和platform总线。如果大家想深入了解这个模型的话,可以自己模拟写一个属于自己的总线、创建自己的设备和驱动。这里也给大家几个资料。
1、从零开始写个platform平台总线
2、字符驱动移植–bus(平台总线), device, driver
3、总线设备驱动模型
三、有关驱动的虚拟内存文件系统—Sysfs系统
在linux系统中除了一些真正存储在介质上的文件系统(如ext2、ext3等)外,还有一种叫做虚拟内存的文件系统,这种文件系统只存在于内存中,断电了,文件系统也就消失了。这种文件系统的作用大多是导出内核的信息,使内核空间和用户空间进行交互。如proc、tmpfs、sysfs等。
对于sysfs文件系统,它最主要的作用就是管理统一的设备、驱动。所以这个文件系统里大都是和驱动相关的。
嗯。。对,这部分还有一些class等,udev等知识点,我还没有搞的太透彻, 先是这些吧。