10_linux dirver platform 框架

1. 概述  

硬件上修改gpio端口后,为了让linux driver 代码修改尽量少,可以使用linux driver 的platform框架。

该框架让硬件dev和软件driver 分离开,尽量保证driver编写完成后,不需要再修改。

如果采用platform机制实现驱动程序, 驱动工程师只需维护两个数据结构, 分别初始化和注册硬件节点和软件节点
struct platform_device
struct platform_driver

内核会帮做四件事:
1.帮你遍历dev或者drv链表
2.帮你调用总线提供的match进行匹配
3.如果匹配成功,内核还会帮你调用软件节点的probe函数
4.如果匹配成功,内核还会给probe函数传递匹配成功的硬件节点首地址

2. dev 信息

详解struct platform_device
struct platform_device {
const char *name;
int id;
struct device dev;
u32 num_resources; // 目前不使用
struct resource * resource; // 目前不使用
};
功能: 描述dev链表上每个纯硬件节点属性
name: 指定一个硬件节点名称,用于匹配,必须初始化
id: 指定一个硬件节点编号,如果dev链表上只有一个名称为name的硬件节点,id=-1;
如果dev链表上有多个同名的硬件节点, 通过id进行标识:id=0,1,2,....
dev: 只需关注其中的void *platform_data字段用于装载驱动工程师自定义的纯硬件信息
struct device { void *platform_data; };
num_resources:用于指示resource描述的硬件信息的个数,即:num_resources = ARRAY_SIZE(led_res)
切记:num_resources只能和resource一起使用

resource:用于装载resource描述的硬件信息, 此数据结构struct resource是内核声明的

总结:装载硬件信息的方法有两种,目前采用第一种方法
1.自定义描述
2.resource描述
两种方法可以同时使用,也可以单独使用

配套函数:
int platform_device_register(struct platform_device *pdev);
功能:向内核dev链表添加硬件节点, 此时内核会帮你遍历,匹配,调用probe,传递参数

void platform_device_unregister(struct platform_device *pdev);
功能:从内核dev链表删除硬件节点对象

对应的头文件: <linux/platform_device.h>

示例代码:

$cat led.h 
#ifndef __LED_H_
#define __LED_H_

struct led_resource {
    char *name;
    int gpio;
};

struct led_platform_data {
    struct led_resource *pres;
    int numbs;
};

#endif

$cat dev_led.c 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include "led.h"

// 定义
struct led_resource led_res[] = {
    {.name = "LED1",   .gpio = PAD_GPIO_C + 12},
    {.name = "LED2",   .gpio = PAD_GPIO_C +  7},
    {.name = "LED3",   .gpio = PAD_GPIO_C + 11},
    {.name = "LED4",   .gpio = PAD_GPIO_B + 26},
};

struct led_platform_data led = {
    .pres = led_res,
    .numbs = ARRAY_SIZE(led_res)
};

void led_release(struct device *dev) {}

struct platform_device led_dev = {
    .name = "tarena_led",
    .id = -1,
    .dev = {
        .platform_data = &led,
        .release = led_release
    }
};

int led_dev_init(void) {
    int i;
    printk("%s, dev=%p, platform_data=%p\n", __func__, &led_dev, led_dev.dev.platform_data);
    for (i = 0; i < ARRAY_SIZE(led_res); i++) {
        printk("%s, %p\n", led_res[i].name, &led_res[i]);
    }
    platform_device_register(&led_dev);
    return 0;
}

void led_dev_exit(void) {
    platform_device_unregister(&led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

3. drv 信息

详解struct platform_driver
struct platform_driver {
struct device_driver driver;
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
};
功能:描述软件节点信息
driver:只关注其中的char *name字段,用于匹配
struct device_driver {
const char *name;
}
probe:硬件节点和软件节点匹配成功,内核调用
形参pdev指向匹配成功的硬件节点(&led_dev)
remove:删除软件节点或者硬件节点,内核调用此函数
形参pdev指向匹配成功的硬件节点(&led_dev)
只调用一次

配套函数:
//向drv链表添加软件节点
//内核帮你做遍历,匹配,调用probe,传递硬件节点的参数:platform_device
int platform_driver_register(struct platform_driver *drv);

//从drv链表删除软件节点
void platform_driver_unregister(struct platform_driver *drv);

对应的头文件: <linux/platform_device.h>

$cat drv_led.c 
// led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include "led.h"

struct led_platform_data* p_led;

int led_probe(struct platform_device *pdev) {
    int i = 0;
    p_led = pdev->dev.platform_data;

    printk("%s, dev=%p, platform_data=%p, p_led->pres=%p, numbs=%d\n",
            __func__, pdev,  p_led, p_led->pres,  p_led->numbs);
    for (i = 0; i < p_led->numbs; i++) {
        printk("%s,  %p\n",  p_led->pres[i].name,  &p_led->pres[i]);
    }

    return 0;
};

int led_remove(struct platform_device *pdev) {
    return 0;
};

struct platform_driver led_drv = {
    .driver = { .name = "tarena_led"},
    .probe = led_probe,
    .remove = led_remove
};

int led_drv_init(void) {
    platform_driver_register(&led_drv);
    return 0;
}

void led_drv_exit(void) {
    platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

4. 编译测试

$cat Makefile 

# 指定模块名称(最终生成的 .ko 文件名)
obj-m += dev_led.o drv_led.o
all:
    make -C /opt/kernel SUBDIRS=$(PWD) modules
    cp *.ko /opt/rootfs/home/driver
clean:
    make -C  /opt/kernel SUBDIRS=$(PWD) clean

测试信息:

/home/driver # insmod dev_led.ko 
[ 1834.902000] led_dev_init, dev=bf010110, platform_data=bf010300
[ 1834.902000] LED1, bf0102e0
[ 1834.903000] LED2, bf0102e8
[ 1834.904000] LED3, bf0102f0
[ 1834.907000] LED4, bf0102f8
/home/driver # 
/home/driver # insmod drv_led.ko 
[ 1840.374000] led_probe, dev=bf010110, platform_data=bf010300, p_led->pres=bf0102e0, numbs=4
[ 1840.375000] LED1,  bf0102e0
[ 1840.376000] LED2,  bf0102e8
[ 1840.378000] LED3,  bf0102f0
[ 1840.379000] LED4,  bf0102f8
/home/driver # 

从测试信息看,dev文件中定义的硬件资源,都可以在drv文件中获取到。如果之后修改gpio,在dev 中修改就可以。

如果要操作gpio,可以在drv 文件中 创建字符设备或混杂设备,从而操作gpio

posted @ 2025-06-11 23:02  靖意风  Views(24)  Comments(0)    收藏  举报