linux驱动之platform设备驱动实验

概述

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函数。
在这里插入图片描述

发布了49 篇原创文章 · 获赞 76 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/fengweibo112/article/details/103214827