需要阅读的前面写的博文
阅读本文前,请先阅读下面这篇博文:
请先通过上面这篇博文对Linux的总线设备驱动的思想有个基本了解。
本文利用总线设备驱动中的Platform总线设备驱动实现对多个LED的驱动。
既然是用Platform总线设备驱动,那就先对Platform总线设备驱动有个详细了解,然后再开始写代码吧。
Platform总线设备驱动的详细介绍
Platform 总线设备驱动是一种基于 Linux 驱动模型的驱动框架,专门用于管理片上系统 (SoC) 内部的硬件资源。这种框架非常适合开发嵌入式设备中与具体硬件总线无关的驱动程序,例如板载 LED、GPIO、按键等。
1. Platform 总线设备驱动的基本概念
-
Platform 总线 (Platform Bus)
是一种虚拟总线,与硬件无关,用于匹配设备(platform_device
)和驱动(platform_driver
)。 -
Platform 设备 (Platform Device)
表示具体的硬件资源,例如板载的 LED、UART 控制器等。 -
Platform 驱动 (Platform Driver)
实现对特定硬件资源的操作逻辑。
设备和驱动匹配
- 匹配依据:设备和驱动通过名称(
platform_device.name
和platform_driver.driver.name
)进行匹配。 - 匹配机制:内核在设备注册时,会检查已注册的驱动;反之,在驱动注册时,会检查已注册的设备。如果匹配成功,就调用驱动的
probe
方法初始化设备。
2. Platform 总线设备驱动的主要组成部分
1. Platform 设备 (struct platform_device)
platform_device
是内核中用于描述硬件资源的结构体,定义了设备的基本属性。
关键字段:
struct platform_device {
const char *name; // 设备名称,用于匹配驱动
int id; // 设备 ID,用于区分多个同类设备
struct device dev; // 嵌入式的通用设备结构体
const struct resource *resource; // 硬件资源(如内存、I/O 等)
unsigned int num_resources; // 硬件资源的数量
};
注册接口:
- 动态注册:
platform_device_register()
,将设备注册到系统。 - 静态注册:通过设备树或内核代码直接描述设备。
2. Platform 驱动 (struct platform_driver)
platform_driver
是内核中用于描述驱动程序的结构体,定义了驱动的操作方法。
关键字段:
struct platform_driver {
int (*probe)(struct platform_device *); // 匹配设备时调用,完成设备初始化
int (*remove)(struct platform_device *); // 设备卸载时调用
void (*shutdown)(struct platform_device *); // 系统关机时调用
struct device_driver driver; // 通用驱动结构体
};
注册接口:
platform_driver_register()
:将驱动注册到系统。platform_driver_unregister()
:从系统中注销驱动。
3. Platform 总线设备驱动的工作流程
-
注册 Platform 设备:
- 定义设备的基本信息,如名称、ID 和硬件资源。
- 调用
platform_device_register()
注册设备。 - 或者,通过设备树描述设备并由内核解析。
-
注册 Platform 驱动:
- 定义驱动的基本信息,如名称和操作函数(
probe
、remove
)。 - 调用
platform_driver_register()
注册驱动。
- 定义驱动的基本信息,如名称和操作函数(
-
设备和驱动的匹配:
- 内核根据设备名称自动匹配设备和驱动。
- 匹配成功后,调用驱动的
probe
方法初始化设备。
-
操作设备:
- 在
probe
方法中完成设备的初始化和资源申请。 - 用户通过设备文件或内核接口访问设备。
- 在
-
卸载设备或驱动:
- 当设备被移除或驱动被卸载时,调用驱动的
remove
方法释放资源。
- 当设备被移除或驱动被卸载时,调用驱动的
4. Platform 总线设备驱动的代码示例
1. Platform 设备
定义和注册一个设备:
#include <linux/platform_device.h>
// 硬件资源描述
static struct resource led_resources[] = {
{
.start = 0x12340000, // 假设的硬件地址
.end = 0x123400FF,
.flags = IORESOURCE_MEM,
},
};
// 定义设备
static struct platform_device led_device = {
.name = "led", // 设备名称
.id = -1, // 通常为 -1,表示无具体 ID
.resource = led_resources,
.num_resources = ARRAY_SIZE(led_resources),
};
// 注册设备
static int __init led_device_init(void) {
return platform_device_register(&led_device);
}
module_init(led_device_init);
// 注销设备
static void __exit led_device_exit(void) {
platform_device_unregister(&led_device);
}
module_exit(led_device_exit);
MODULE_LICENSE("GPL");
2. Platform 驱动
定义和注册一个驱动:
#include <linux/platform_device.h>
#include <linux/module.h>
// probe 方法:设备匹配成功时调用
static int led_probe(struct platform_device *pdev) {
struct resource *res;
printk("LED device probed!\n");
// 获取硬件资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk("No memory resource found!\n");
return -ENODEV;
}
printk("Resource start: %lx, end: %lx\n", res->start, res->end);
return 0;
}
// remove 方法:设备移除时调用
static int led_remove(struct platform_device *pdev) {
printk("LED device removed!\n");
return 0;
}
// 定义驱动
static struct platform_driver led_driver = {
.driver = {
.name = "led", // 匹配设备的名称
},
.probe = led_probe,
.remove = led_remove,
};
// 注册驱动
static int __init led_driver_init(void) {
return platform_driver_register(&led_driver);
}
module_init(led_driver_init);
// 注销驱动
static void __exit led_driver_exit(void) {
platform_driver_unregister(&led_driver);
}
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
5. Platform 总线设备驱动的优点
- 灵活性:适合片上资源管理,可通过名称直接匹配。
- 简单性:框架简单易用,不需要实现复杂的总线协议。
- 适应性:支持静态注册和设备树描述,适应多种开发需求。
- 统一性:与其他标准总线驱动一致,便于管理和扩展。
6. Platform 总线设备驱动的典型应用
- GPIO 驱动:板载按键或 LED。
- PWM 驱动:用于控制风扇或亮度调节。
- UART 驱动:串口控制器。
- ADC/DAC 驱动:模数/数模转换器。
总结:
Platform 总线设备驱动通过虚拟总线实现了硬件资源和驱动程序的解耦,特别适合嵌入式开发中的片上资源管理,是 Linux 驱动模型的重要组成部分。
完整源代码
实现platform_driver
的源码(chip_demo_gpio.c)
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include "led_opr.h"
#include "leddrv.h"
#include "led_resource.h"
static int g_ledpins[100];
static int g_ledcnt = 0;
static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{
//printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
switch(GROUP(g_ledpins[which]))
{
case 0:
{
printk("init pin of group 0 ...\n");
break;
}
case 1:
{
printk("init pin of group 1 ...\n");
break;
}
case 2:
{
printk("init pin of group 2 ...\n");
break;
}
case 3:
{
printk("init pin of group 3 ...\n");
break;
}
}
return 0;
}
static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
//printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
switch(GROUP(g_ledpins[which]))
{
case 0:
{
printk("set pin of group 0 ...\n")