关于Linux平台设备驱动模型,并不是创建新的设备分类,是在原有的字符设备基础上使用,将设备和驱动分开,生成两个.ko文件。
Linux内核维护一个全局设备链表,对应的总线会将驱动和设备链表里的设备名进行匹配,如果匹配成功就会将设备的信息传递给驱动的probe函数,probe函数得到设备的核心结构体platform_device的信息就可以进行对应的操作。
我们只需实现平台驱动和平台设备即可,平台总线是内核实现的,常见的总线如IIC、SPI、CAN等,LED、KEY这类型的普通字符设备,linux内核就使用虚拟的平台总线struct bus_type platform_bus_type来匹配这类设备。
首先来看platform_device平台设备层结构体。
我们只需要关注name、id、num_resources、resource、dev这几个成员。
struct platform_device{
const char *name; //自定义的设备名,用于和驱动匹。
int id; //当设备和驱动一对一匹配时,所设值为-1
struct device dev; //下面列举其成员介绍。该结构体下的release函数是必须实现的
u32 num_resources; //设备的个数,即设备资源数组的元素个数
struct resource *resource; //设备所用资源数组的首地址
};
接下来看platform_device下的resources结构体。
我们需关注前四个成员,最后一个成员是由内核实现。
struct resource{
resource_size_t start; //所用资源的起始物理地址
resource_size_t end; //所用资源的结束物理地址
const char *name; //自定义的资源名称,此名称不与驱动相匹配,可以随便写,但要有意义
unsigned long flags; //所用资源的类型,例如中断IORESOURCE_IRQ,外设或者用于和设备通讯的支持直接寻址的地址空间IORESOURCE_MEM
};
接下来看platform_device结构体下的struct device,由于结构体过大,只展示重要的部分。
struct device{
void *platform_data; //这个指针指向设属性信息,platform_driver可以获取该数据
void *driver_data ; //用来告诉驱动需要服务的设备的类型
void (*release)(struct device *dev); //释放平台设备时会调用此函数,必须要实现
}
因此,编写平台设备的步骤大概如下:
1、分析硬件原理图,写出所使用的设备的占用资源情况,例如GPIO0寄存器的起始物理地址,GPIO0寄存器所占用的空间大小
2、定义并初始化一个资源结构数组struct resource,存放所有使用到的设备的信息。
3、编写一个release函数,在释放平台设备device时会调用此函数。
4、定义并初始化一个平台设备核心结构struct platform_device,其下的name是用来与driver下的name匹配的。
5、在模块初始化函数中调用注册平台设备函数,将平台设备核心结构体注册到内核中。
6、在模块卸载函数中调用注销平台设备函数,将平台设备核心结构体从内核中注销。
接下来实现平台驱动platform_driver核心结构体。
我们只需关注要用到的成员。
struct platform_driver {
int (*probe)(struct platform_device *); //指向设备探测函数,当与设备相匹配时调用此函数,参数是将平台设备指针传入,通过操作平台设备指针可以初始化硬件。是很重要的函数
int (*remove)(struct platform_device *);//指向移除设备函数,当总线上的设备和驱动匹配关系解除时会调用此函数。如果probe函数中申请了资源,就需要在此函数中按顺序释放
struct device_driver driver; //此结构体下的name成员和设备相匹配,还有一个成员是用于和设备树相匹配,下面会列出
const struct platform_device_id *id_table; /如果需要匹配多个设备,也可以使用此成员
};
接下来看platform_driver下的device_driver结构体。
我们只需关注重要的成员。
struct device_driver{
const char *name; //此名字会和平台设备的名字相匹配
const struct of_device_id *of_match_table; //此成员下的compatible是和设备的名字相匹配,下面会列出。是用于设备树的方式实现一个驱动匹配多个设备
}
接下来关注platform_driver下的platform_device_id。
struct platform_device_id {
char name[PLATFORM_NAME_SIZE]; //此成员用于和设备名相匹配
kernel_ulong_t driver_data; //此成员用于来保存设备的配置
};
因此,平台驱动有三种方式和平台设备相匹配。
第一种:也就是优先级最低的一种。platform_driver下的name成员和平台设备platform_device下的name成员匹配。
第二种:优先级第二高。平台设备platform_device下的name成员和platform_driver下的platform_device_id所指向的数组的每个元素的name匹配。
第三种:优先级最高,使用的是设备树方式。platform_driver下的device_driver下的of_device_id所指向的每个元素的compatible值与设备树中设备节点的compatibl值相匹配。
这里的优先级是指,当设备树方式实现时,会先用设备树方式匹配,当设备树无法匹配时会使用platform_device_id匹配,最后再使用platform_driver下的name成员匹配。
因此,平台驱动层的编写大概如下:
1、定义一个probe探测函数和remove删除函数。
2、如果需要用到platform_device_id,则需要定义一个platform_device_id的数组来存储相应的设备信息。如果不需要用到则跳过。
3、如果需要用到设备树匹配,则定义一个of_device_id数组来存储设备的信息和数据。如果不需要用到则跳过。
4、定义一个平台设备驱动核心结构体platform_driver并初始化里面需要用到的成员。
5、在模块初始化函数中调用注册平台驱动函数。
6、在模块卸载函数中调用注销平台驱动函数。
一般probe探测函数框架如下:
1、获取平台设备的属性信息、数据,利用传进来的指针可以得到。
2、探测资源。
3、申请资源
4、使用资源,映射寄存器、申请中断资源等。
5、初始化硬件设备。
6、注册input子系统设备、字符设备、块设备、网络设备等。
remove函数就将probe函数中所申请占用的资源按顺序进行释放。
今天的文章Linux内核平台设备驱动模型platform_device和platform_driver框架分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/25804.html