概述
platform机制:把硬件相关的代码(固定的,如板子的网卡、中断地址)和驱动(会根据程序作变动,如点哪一个灯)分离开来,即要编写两个文件:dev.c和drv.c(platform设备和platform驱动)
platform会存在/sys/bus/里面,如下图所示, platform目录下会有两个文件,分别就是platform设备和platform驱动
驱动的分离,引出了总线(bus)、驱动(driver)和设备(device)模型。
基础知识
(一) 编写设备代码需要用到的结构体和函数
platform_device结构体如下:
struct platform_device {
const char * name; //设备名称,要与platform_driver的name一样,这样总线才能匹配成功
u32 id; //id号,插入总线下相同name的设备编号(一个驱动可以有多个设备),如果只有一个设备填-1
struct device dev; //内嵌的具体的device结构体,其中成员platform_data,是个void *类型,可以给平台driver提供各种数据(比如:GPIO引脚等等)
u32 num_resources; //资源数量,
struct resource * resource; //资源结构体,保存设备的信息
};
要用的函数如下,在dev设备的入口出口函数中用到
int platform_device_register(struct platform_device * pdev); //注册dev设备
int platform_device_register(struct platform_device * pdev); //注销dev设备
(二) 编写驱动代码需要用到的结构体和函数
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 *); //唤醒
struct device_driver driver; //内嵌的driver,其中的name成员要等于设备的名称才能匹配
};
int platform_driver_register(struct platform_driver *drv); //注册驱动
platform_driver_unregister(struct platform_driver *drv); //卸载驱动
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num);//获取设备的某个资源,获取成功,则返回一个resource资源结构体
platform编写LED驱动
首先创建设备代码和驱动代码:led_dev.c(platform设备,指定灯的引脚)、led_drv.c(platform驱动,初始化灯和控制逻辑)
(一) 编写led_dev.c
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
static struct resource led_resource[] = { //资源数组
[0] = {
.start = 0x56000050, //led的寄存器GPFCON起始地址
.end = 0x56000050 + 8 - 1, // led的寄存器GPFDAT结束地址
.flags = IORESOURCE_MEM, //表示地址资源
}
};
static void led_release(struct device * dev) //释放函数
{}
static struct platform_device led_dev = {
.name = "myled", //对应的platform_driver驱动的名字
.id = -1, //表示只有一个设备
.num_resources = ARRAY_SIZE(led_resource), //资源数量,ARRAY_SIZE()函数:获取数量
.resource = led_resource, //资源数组led_resource
.dev = {
.release = led_release, //释放函数,必须向内核提供一个release函数, 、
//否则卸载时,内核找不到该函数会报错
},
};
static int led_dev_init(void) //入口函数,注册dev设备
{
platform_device_register(&led_dev);
return 0;
}
static void led_dev_exit(void) //出口函数,注销dev设备
{
platform_device_unregister(&led_dev);
}
module_init(led_dev_init); //修饰入口函数
module_exit(led_dev_exit); //修饰出口函数
MODULE_LICENSE("GPL"); //声明函数
(二) 编写led_drv.c
#include <linux/fs.h> /*包含file_operation结构体*/
#include <linux/init.h> /* 包含module_init module_exit */
#include <linux/module.h> /* 包含LICENSE的宏 */
#include <linux/miscdevice.h>/*包含miscdevice结构体*/
#include <linux/kernel.h> /*包含printk等操作函数*/
#include <linux/platform_device.h>
static int led_probe(struct platform_device *pdev)
{
printk("enter probe\n");
return 0;
}
static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
printk("enter remove\n");
return 0;
}
struct platform_driver led_drv = {
.probe = led_probe, //当与设备匹配,则调用该函数
.remove = led_remove, //删除设备
.driver = {
.name = "myled", //与设备名称一样
}
};
static int led_drv_init(void) //入口函数,注册驱动
{
platform_driver_register(&led_drv);
return 0;
}
static void led_drv_exit(void) //出口函数,卸载驱动
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
运行测试
如下图,“insmod led_dev.ko”加载设备成功过后,在/sys/bus/platform/devices目录下会生成对应的设备名称。
加载驱动时,当和设备名字“myled”匹配成功过后,会进入对应的probe函数。