本文在已向平台总线注册设备“hello_ctl”的基础上展开,注册设备到平台总线的流程可参见上一篇(嵌入式Linux系统:驱动编程_注册设备到平台总线 )。
在 Linux2.6 之后,Linux 设备驱动分为三个实体:总线、设备、驱动,平台总线将设备和驱动匹配。在系统注册任意一个驱动的时候,都会寻找对应的设备;当系统注册设备的时候,系统也会寻找对应的驱动进行匹配。
在 Linux 系统中,任何一个 Linux 设备和 Linux 驱动都是需要挂载到一种总线中。有一些常规的大家容易理解的总线,例如 media 总线、spi 总线、hid 输入子系统总线、eMMC 存储设备总线等等。假如说设备本身就是一个总线设备,那么挂载到对应的总线上,那是容易理解的。
但是还有一些例如 led、蜂鸣器等等一些设备,都不是从字面上理解的总线设备。针对这个情况,Linux 创立了一种虚拟总线,也叫平台总线或者 platform 总线,这个总线也有对应的设备 platform_device,对应的驱动叫 platform_driver。
这里介绍的平台总线,不能够直接和常规的总线对应,只是 Linux 系统提供的一种附加手段,防止 linux 驱动的碎片化,降低 Linux 的使用难度。
下图是总线、设备、驱动的框架图。
这里只讨论先注册设备再注册驱动的情况。在注册驱动的时候,系统会通过 platform_match 函数匹配设备和驱动。
注册设备的结构体为 platform_device,注册驱动的结构体为 platform_driver。设备和和驱动结构体的成员 name 字段,相同则匹配。如果匹配了则会调用 platform_driver 中的 probe 函数,注册驱动。也就是在上图注册驱动的时候要添加一个判断,如下图所示。
驱动注册——头文件
驱动注册使用结构体platform_driver,该结构体在头文件“include/linux/platform_device.h”中定义。
驱动注册platform_driver_register,驱动卸载函数platform_driver_unregister也在这个头文件中– 这两个函数的参数都只有结构体platform_driver。
驱动注册——注册结构体
驱动常见的几种状态,初始化,移除,休眠,复位– 就像PC一样,有的驱动休眠之后无法使用,有的可以使用;有的系统唤
醒之后,驱动需要重新启动才能正常工作,也有直接就可以使用等等。
probe函数– platform_match函数匹配之后,驱动调用的初始化函数。
remove函数– 移除驱动函数。
suspend函数– 悬挂(休眠)驱动函数。
resume函数– 休眠后恢复驱动。
device_driver数据结构的两个参数:– name和注册的设备name要一致;– owner一般赋值THIS_MODULE。
如下,是完整的注册驱动到平台总线的代码:probe_linux_module.c
以下代码实现的功能是将驱动“hello_ctl”注册到平台总线,前提是已将设备“hello_ctl”注册到平台总线(见博文:嵌入式Linux系统:驱动编程_注册设备到平台总线 )。
#include <linux/init.h>
#include <linux/module.h>
/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
#define DRIVER_NAME "hello_ctl"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("santiren");
static int hello_probe(struct platform_device *pdv){
printk(KERN_EMERG "\tinitialized\n");
return 0;
}
static int hello_remove(struct platform_device *pdv){
return 0;
}
static void hello_shutdown(struct platform_device *pdv){
;
}
static int hello_suspend(struct platform_device *pdv){
return 0;
}
static int hello_resume(struct platform_device *pdv){
return 0;
}
struct platform_driver hello_driver = {
.probe = hello_probe,
.remove = hello_remove,
.shutdown = hello_shutdown,
.suspend = hello_suspend,
.resume = hello_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int hello_init(void)
{
int DriverState;
printk(KERN_EMERG "HELLO WORLD enter!\n");
DriverState = platform_driver_register(&hello_driver);
printk(KERN_EMERG "\tDriverState is %d\n",DriverState);
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "HELLO WORLD exit!\n");
platform_driver_unregister(&hello_driver);
}
module_init(hello_init);
module_exit(hello_exit);
代码中,定义的宏变量 DRIVER_NAME “hello_ctl”,和前面注册的 hello 设备的名称相同。
设备和驱动匹配成功就会进入函数 hello_probe 打印“initialized”。
Makefile 文件如下图所示。
在 Ubuntu 中的目录“/home/topeet”中新建目录“probe_linux_module”,拷贝驱动文件“probe_linux_module.c”和编译文件“Makefile”到新建中,如下图所示。
进入“probe_linux_module”目录,使用命令“make”编译“probe_linux_module.c”,如下图所示,生成模块文件“probe_linux_module.ko”。
启动开发板,拷贝“probe_linux_module.ko”到 U 盘,将 U 盘插入开发板,加载驱动文件“probe_linux_module.ko”,如下图所示,可以看到打印出了“initialized”,表明进入了probe 函数。
使用命令“rmmod probe_linux_module”卸载驱动,可以看到打印“HELLO WORLD exit!”,表明卸载成功。