对于字符设备驱动程序,之前都是在驱动程序中定义并设置file_operations结构体,实现各种需要用到的函数,注册file_operations结构体,框架比较简单,但是在Linux中,却很少看见这样框架的代码,那是因为在Linux中设备驱动模型一般都由总线、设备、驱动这个三大部分组成。这是一种分层分离的思想:

分层:核心层和设备相关层分开。

分离:讲硬件相关的代码和驱动分开。

这就是总线设备驱动(bus_drv_dev)模型:

对于dev模块,当调用device_add函数时:

a.会将device结构体放入bus的device链表

b.从bus的drv链表中取出每一个结构体,利用match函数进行对比,判断是否当前的这个dev

c.如支持,则调用驱动额probe函数

对于drv模块,当调用driver_register时:

a.会将driver结构体放入bus的driver结构体

b.从bus的dev结构体中取出每一个结构体,通过match函数比较,判断是否支持当前driver

c.若支持,这调用驱动的probe函数

wKioL1aD7kDDTlFTAACC3oPESbU113.png

match函数的比较:

match函数是根据device中的bus_id和driver的name是否一致来匹配的,如果一样就调用driver中的probe函数


编写驱动的步骤:

一.在device部分(device.c)

1.定义并设置一个platform_device结构体:包括name(要和driver中的名字对应)、dev结构体下完成一个release函数、一个resource结构体(需要自己定义并填充),这个结构体中有着硬件相关的地址

如:static resource led_dev_resource[]={

[0]={

.start=0x56000050,

.end=0x56000050+4-1,

.flags=IORESOURCE_MEM,

},

[1]={

.start=4,

.end=4,

.flags=IORESOURCE_IRQ,

},

};

static void led_dev_release(struct device * dev)

{

}

static platform_device led_dev={

.name = "myled",

.id = -1,

.resource=led_dev_resource,

.dev={

.release=led_dev_release,

},

};

2.注册platform_device结构体


int platform_device_register(struct platform_device * pdev)//注册platform_device结构体

void platform_device_unregister(struct platform_device * pdev)//卸载


二.在driver部分(driver.c)

1.定义并设置一个platform_driver结构体,

如:static platform_driver led_drv={

.probe=led_drv_probe,//匹配后讲调用的函数

.remove=led_drv_remove,//与probe相关的清理工作

.driver={

.name="myled",//名字必须与platform_device中的name相同

.owner=THIS_MODULE,

},

};

2.注册

int platform_device_register(struct platform_device * pdev)

例子:讲led点灯拆分成dev和dri两个部分

在dev.c中(部分核心代码)

wKiom1aD8L2iKfPrAAJXjTYGMHc358.png

在drv中(部分代码)

wKiom1aD8hWhr8zSAAIzN00siuQ835.png

wKioL1aD8pbRW1VPAAJ74hS6dmk448.png

wKiom1aD8qTgzP4aAAEjw2z9B9M464.png

整体上实现了一个bus-dev-drv模型的驱动程序,这样做的好处:当我们需要修改硬件的时候,只需要修改dev中的代码即可,不需要动dri中的代码。