目录
平台总线模型也叫platform总线模型,是linux内核虚拟出来的一条总线,不是真实的导线
平台总线是以名字来匹配的,实际上就是字符串比较,
主要就是把原来的驱动.c文件分为两个c文件,一个驱动,c,一个设备,c
- 把稳定不变的放到驱动,c
- 需要变得放在设备.c
- 一个是 device.c,一个是 driver.c,然后分别注册 device.c和 driver.c
- device是设备,主要处理与具体设备相关的配置和操作。这个文件通常包含平台设备的定义、资源分配、设备初始化以及设备注册等操作
- 定义设备的硬件资源,如 I/O 端口、内存区域、中断号等
- 设备初始化和退出函数。这些函数负责在设备加载和卸载时,进行硬件初始化和资源释放
- 通过
platform_device_register
函数将设备注册到内核中,使得设备可以被相应的驱动程序识别并操作
- driver是驱动,处理驱动程序的实现,负责与设备进行交互,管理设备的生命周期(例如初始化和清理),并提供设备与操作系统之间的接口
- 定义驱动程序的
probe
和remove
回调函数,这些函数在设备被检测到和移除时被调用 - 处理设备的注册和注销。通过
platform_driver_register
和platform_driver_unregister
函数将驱动程序注册到内核中 - 实现设备的操作逻辑,如打开、关闭、读写操作等
- 定义驱动程序的
- device是设备,主要处理与具体设备相关的配置和操作。这个文件通常包含平台设备的定义、资源分配、设备初始化以及设备注册等操作
作用
- 可以提高代码的重用性
- 减少重复性代码
优点
- 驱动和设备代码分开
- 都挂在总线上,方便管理
注册platform设备
平台总线注册一个 device.c
device.c里面写的是硬件资源,这里的硬件资源是指寄存器的地址,中断号,时钟等硬件
资源。
在linux中是用一个结构体来描述硬件资源,定义在linux/include/platform_device.h中
struct platform_device{
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override;
struct mfd_cell *mfd_cell;
struct pdev_archdata archdata;
};
- 主要参数
- name:平台总线进行匹配的时候用到的name,并且在sys/bus/platform.devices/目录下会生成一个name文件
- id:设备id,一般为1,相同name的设备编号
- dev:设备通用储存的部分,内嵌的device机构提
- numresources:资源的个数
- resources:硬件资源
struct resource{ resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent,*sibling,*child; };
- start:资源的起始
- end:资源的结束
- name:资源的名字
- flags:资源的类型
- IORESOURCE_IO IO的内存
- IORESOURCE_MEM 物理内存
- IORESOURCE_IRQ 中断
用蜂鸣器举一个例子device.c
目标
- 加载时注册一个名为
beep_test
的平台设备,并在卸载时注销该设备
步骤
- 新建一个device.c
- 先定义一个平台设备 beep_device 结构体
- 设备的名称
beep_test
- 设备 ID
1
- 设备的资源
beep_res
- 资源的数量
num_resources
- 释放函数也在
dev
字段中指定为beep_release
- 设备的名称
- 定义一个设备资源 beep_res 结构体
beep_res
是一个资源数组,用于描述设备使用的硬件资源- 只定义了一个资源
- start 起始地址为不同芯片的数据寄存器的起始地址
- end 结束地址为不同芯片的数据寄存器的结束地址
flags
设置为IORESOURCE_MEM
,表示这是一个内存资源
- 定义一个设备 beep_release 释放函数
beep_release
函数是平台设备beep_device
的释放函数,在设备被卸载时调用
- 模块 beep_init 初始化和 beep_exit 退出函数
platform_device_register
注册平台设备beep_device
platform_device_unregister
注销平台设备beep_device
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
void beep_release(struct device *dev)
{
printk("beep_release\n");
};
struct resource beep_res[] =
{
[0] = {
.start = 0x0000000,
.end = 0x0000000,
.flags = IORESOURCE_MEM,
.name = "GPIO5_DR"
}
};
struct platform_device beep_device =
{
.name = "beep_test",
.id = 1,
.resource = beep_res,
.num_resources = ARRAY_SIZE(beep_res),
.dev = {
.release = beep_release
}
};
static int beep_init(void)
{
printk("hello beep\n");
return platform_device_register(&beep_device);
}
static void beep_exit(void)
{
printk("beep goodbye");
platform_device_unregister(&beep_device);
}
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
注册platform驱动driver.c
- 首先定义一个 platform driver 结构体变量
- 然后去实现结构体中的各个成员变量
- 那么当我们的 driver 和 device 匹配成功的时候,就会执行 platform_driver 结构体中的 probe 函数,所以匹配成功以后的重点就在于 probe 函数的编写。
注册一个driver.c驱动
platform_driver 结构体
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 (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
}
- 最主要的就是probe函数,可以在driver与device匹配成功后,注册杂项设备或者点个灯都可以
- remove:当driver与device其中一个remove时,就会调用
- shutdown:当设备收到shutdown命令的时候,就会调用
- suspend:当设备收到suspent(休眠)命令的时候,就会调用
- resume:当设备收到resume(唤醒)命令的时候,就会调用
- driver:
struct device_driver { const char*name; struct bus_fype *bus; struct module *owner; const char *mod_name; bool suppress_bind_attrs; const struct of_device_id *of_match_table;; const struct acpi_device_id *acpi_match_table; int(*probe)(struct device *dev); int(*remove)(struct device *dev); void(*shutdown)(struct device *dev); int(*suspend)(struct device *dev,pm_message_t state); int(*resume)(struct device *dev); const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p; };
- 在这里我们只用到name和owner和of_match_table三个成员
- name:匹配设备的名字,应该与上面的device.c里的platform_device结构体里的name相匹配,要不然driver与device不会互相匹配
- owner:为THIS_MODULE
- of_match_table:
- id_table:platform_device_id 结构体里还有一个name成员,与driver的name意义一样,如果driver和id_table都有name,会优先匹配id_table的name
- prevent_deferred_probe:
用蜂鸣器举一个例子driver.c
目标
- 定义了一个平台驱动程序,在init中注册,用于管理一个名为 "beep_test" 的设备,有匹配到的话就会输出,
步骤
- 新建一个driver.c
- 定义一个平台驱动platform_driver结构体
- 定义platform_driver结构体中的平台设备与平台驱动程序匹配函数
- 定义platform_driver结构体中的平台设备被移除或驱动程序卸载函数
- 定义platform_driver结构体中的driver成员
- 赋值需要匹配的name,并与device中的name相同
- 赋值owner为THIS_MODULE
- 定义platform_driver结构体中的id_table成员,会优先匹配id_table里的name,如果查不到id_table里的name就不会有匹配的设备,就不会调用probe函数(可以自己定义来试一试)
- 在intit函数中将平台驱动注册到内核中,内核会根据这个结构体中的信息来管理与其关联的平台设备
- 在exit函数中进行注销,如果有匹配的设备,此时会调用
remove
函数
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
int beep_probe(struct platform_device *pdev)
{
printk("beep_probe\n");
return 0;
}
int beep_remove(struct platform_device *pedv)
{
printk("beep_remove\n");
return 0;
};
//const struct platform_device_id beep_id_table =
//{
// .name = "123"
//};
struct platform_driver beep_device =
{
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.owner = THIS_MODULE,
.name = "beep_test"
},
//.id_table = &beep_id_table
};
static int beep_driver_init(void)
{
int ret = 0;
ret = platform_driver_register(&beep_device);
if (ret<0)
{
printk("platform_driver_register error\n");
return ret;
}
printk("platform_driver_register ok\n");
return 0;
}
static void beep_driver_exit(void)
{
printk("beep goodbye");
platform_driver_unregister(&beep_device);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
运行步骤
- 将上面的device.c和driver.c编译成模块
- 需要注意的是device和driver中的name必须相同才能匹配
- 加载这两个device和driver模块就可以看到匹配成功函数的输出 beep_probe
- 需要注意的是无论先加载哪个都可以
- 成功后就可以在probe函数中写需要完成的动作了,比如控制哪个io口,创建什么设备什么的