Linux设备驱动模型

Linux设备驱动模型关注总线、设备和驱动的交互,通过总线实现设备与驱动的匹配。平台总线(platform)是为了解决嵌入式系统中非总线设备的问题,提供统一的设备模型和资源管理。设备和驱动的注册过程涉及probe和remove函数,资源通过resource结构体管理。在设备驱动编写中,驱动归属于特定总线,设备和驱动的匹配基于PID、VID等标识。
摘要由CSDN通过智能技术生成

        在Linux的设备驱动模型中,关心总线、设备和驱动这 3 个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成,设备和驱动谁先加载,最终都会通过总线来匹配设备和驱动。

        一个现实的 Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在 SoC内存空间的外设等确不依附于此类总线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,除了本身依附于某类总线的设备外,其他设备的驱动都可以看做虚拟总线设备,相应的设备称为platform_device,而驱动成为platform_driver。引入platform 的意义:

(1)使得设备被挂接在一个总线上,因此,非总线设备也符合 Linux的设备模型。其结果是,配套的sysfs 结点、设备电源管理都成为可能。

(2)隔离BSP和驱动,在BSP中定义platform设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用 API 去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

总线:各类总线如:PCIUSBI2CSPI以及虚拟platform总线;

设备:在板级文件(BSP)添加,是与具体平台有关的资源(中断源,IO地址等)

驱动:与平台无关的实现,具体硬件操作相关的代码。

       在Linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为Linux内核几乎集成所有总线bus,如PCI、USB、I2C、SPI等。并且总线bus中(与特定硬件相关的代码)已由芯片提供商编写完成。设备驱动层与特定device相干的就需要驱动工程师来实现了。

一、platform设备驱动

1.platform设备结构体

struct platform_device

{

    const char *name;        / * 设备名 */

    u32 id;                  /* 设备ID*/

    structdevice dev;

    u32num_resources;       / * 设备所使用各类资源数量 */

    structresource * resource;  / * 资源*/

};

2.platform驱动结构体

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(*suspend_late) (struct platform_device *, pm_message_t state);

    int(*resume_early) (struct platform_device *);

    int(*resume) (struct platform_device *);

    structpm_ext_ops *pm;

    struct device_driverdriver;

};

3.platform总线结构体

struct bus_type platform_bus_type =

{

  .name ="platform",

  .dev_attrs = platform_dev_attrs,

  .match = platform_match,

  .uevent = platform_uevent,

  .pm = PLATFORM_PM_OPS_PTR,

};

4.设备与驱动匹配

static int platform_match(struct device *dev, structdevice_driver *drv)

{

    structplatform_device * pdev;

    pdev =container_of(dev, struct platform_device, dev);

    return(strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);

}

       从代码看出,匹配 platform_device platform_driver主要看两者的 name字段是否相同,对 platform_device的定义通常在 BSP的板文件中实现。平台驱动的实现很简单,模块加载和卸载函数仅仅通过 platform_driver_register()platform_driver_unregister()函数进行 platform_driver 的注册与注销,而原先注册和注销字符设备的工作已经被移交到platform_driverprobe()remove()成员函数中。

5.平台设备资源

struct resource

{

    resource__size_t start;

    resource_size_t end;

    const char *name;

    unsigned long flags;

    struct resource *parent, *sibling, *child;

};

       通常只需要关心start、end和flags这3个字段,分别标明资源的开始值、结束值和类型,flags可以为 IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA 等。start、end 的含义会随着 flags 而变更,如当flags为 IORESOURCE_MEM 时,start、end分别表示该 platform_device 占据的内存的开始地址和结束地址;当 flags 为 IORESOURCE_IRQ 时,start、end 分别表示该 platform_device 使用的中断号的开始值和结束值,如果只使用了 1 个中断号,开始和结束值相同。对于同种类型的资源而言,可以有多份,例如说某设备占据了 2 个内存区域,则可以定义 2 个 IORESOURCE_MEM 资源。

 

      对 resource 的定义也通常在 BSP 的板文件中进行,而在具体的设备驱动中透过 platform_get_resource()这样的 API 来获取,此 API 的原型为:

struct resource *platform_get_resource(struct platform_device*, unsigned int, unsigned int);

       对于IRQ而言,platform_get_resource()还有一个进行了封装的变体platform_get_irq(),其原型为:

int platform_get_irq(struct platform_device *dev, unsignedint num);


二、设备总线驱动模型

    kobject/kset 是设备模型的基本结构体,设备模型使用这两个结构体来完成设备的层次关系,但在实际的设备驱动编写中,我们基本上用不到kobject/kset这些结构体,是因为这些结构体又被嵌入到更大的结构体中,原因在于kobject/kset结构体只能表征设备的层次关系,但是一个设备的驱动,并不是简单的一个层次关系而已,因此,必需要把kobject/kset结构体嵌入到更大的结构体中,使用kobject/kset来表征层次关系,用其他的成员表示设备 驱动的具体功能。

    在设备模型中,我们将看到,设备驱动主要是由总线,驱动程序,设备三个部分构成,通过这三个标准部件,把各种纷繁杂乱的设备归结过来,达到简化设备驱动编写的目的,也即我们编写的设备驱动,其实也只是这三部分中的一个很小的部分的。

    我们编写的设备驱动程序,一定是先属于一个总线的驱动,比如属于USB总线,或者属于PCI总线,或者属于I2C总线,等等,因为我们编写的设备驱动,在注册、安装到系统时,系统会先检查驱动是属于哪个总线的(设备驱动编写时已经定义好),会把驱动加入到对应的总线的kset中,即把当前设备驱动的kobject加入到对应总线的kset中,形成层次关联。而当系统检测到有设备(硬件)存在,也会先判断设备是属于哪个总线的(硬件连接),然后遍历当前总线下的所有设备驱动程序,通过所属总线的探测函数,查找是否有设备 驱动程序匹配可以驱动当前的设备(一般是通过获得设备的PID,VID,跟驱动程序的PID,VID比较,看是否匹配而定),如果有驱动程序可以驱动设备,则把当前设备也加入到所属总线的kset中,如果没有可驱动设备的驱动程序,则只能在总线的设备链表中存在,而如果设备都无法通过总线的匹配,则也没有办法存在于总线的设备链表中。由于一条总线要管理总线上的所有驱动,同时要管理总线上的有所设备,则需要再把所有设备和所有驱动都分开,分别设立一个设备kset和一个设备驱动kset,用于管理所有的设备和设备驱动,如此,则总线kset实际上包含了两个kset(设备kset,设备驱动kset), 设备kset又包含了所有的当前总线的设备的kobject,设备驱动kset包含了所有的当前总线的设备驱动的kobject;而所有的总线,又形成了bus的kset,归结起来就形成下图的层次关系:

    每个设备,都被挂接到不同的总线上,当设备挂接到对应的总线上 后,其所对应的总线类型就确定了,而设备在挂接到总线上时,总线先要扫描设备,看看设备是否适合总线的要求,如果适合了,那接着就要扫描整个总线上的设备驱动链表,查找是否有驱动程序可以管理设备,如果找到,则把设备结构体中的相应指针成员指向对应的驱动程序,如果暂时没有找到对应的设备驱动程序,则设备结构体中的指向驱动程序的指针暂时为空,表示还没有设备驱动,还在总线的设备队列中等待;而如果设备不能通过总线的检查,即不会出现在总线的设备列表上, 自然不会去扫描设备驱动链表,查找匹配的驱动了。

    而每个设备驱动程序,都是被安装到对应的总线上的,不论是手动安装,还是自动安装,所谓安装,就是把驱动程序挂载到对应总线的驱动链表中,而挂载到对应的总线驱动链表,首先要满足总线的匹配要求,只有适合了要求,才能 挂载到总线的驱动链表,也只有到达这个步骤,系统才会扫描整个总线的设备链表,来查找是否有设备需要此驱动来管理,如果找到这个设备,则驱动程序中的设备 管理链表,会记录这个设备的地址,从而达到管理设备的目的。

经过上述的设备插入,或者驱动安装,系统就会出现只有设备,而没 有设备驱动程序的情况,也会出现,只有设备驱动程序,没有对应的设备的情况,此时,设备或者设备驱动程序,就会暂时在各自的队列里等待,一旦有驱动程序安 装,或新的设备插入,就都会自动的去扫描对应的链表,来检测是否有配对的可能。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值