对于字符设备驱动程序,之前都是在驱动程序中定义并设置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函数
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中(部分核心代码)
在drv中(部分代码)
整体上实现了一个bus-dev-drv模型的驱动程序,这样做的好处:当我们需要修改硬件的时候,只需要修改dev中的代码即可,不需要动dri中的代码。