1.platform介绍:
什么是platform?
平台总线(Platform bus)是 Linux 内核中提供的一种虚拟总线,它充当了平台设备(platform device)和平台驱动(platform driver)之间的桥梁,负责将它们进行匹配和绑定。示意图如下:
为什么需要platform?
kernel\include\linux\device.h
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
void (*sync_state)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*num_vf)(struct device *dev);
int (*dma_configure)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
bool need_parent_lock;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
ANDROID_KABI_RESERVE(3);
ANDROID_KABI_RESERVE(4);
};
platform 总线是 bus_type 的一个具体实例
kernel\drivers\base\platform.c
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops,
};
2.2.platform设备
kernel\include\linux\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; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
2.3.platform驱动
kernel\include\linux\platform_device.h
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;
};
2.4.platform驱动和设备匹配规则
platform_bus_type 结构体中 platform_match 就是匹配函数。我们来看一下驱动和设备是如何匹配的
platform_match 函数定义在文件 drivers/base/platform.c 中,函数内容如下所示:
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
函数分析:
驱动和设备的匹配有四种方法,我们依次来看一下:
- of_driver_match_device(dev, drv) OF 类型的匹配,也就是设备树采用的匹配方式
- acpi_driver_match_device(dev, drv) ACPI 匹配方式
- id_table 匹配,每个 platform_driver 结构体有一个 id_table这些 id 信息存放着这个 platformd 驱动所支持的驱
动类型。
- 如果第三种匹配方式的 id_table 不存在的话就直接比较驱动和设备的 name 字段,如果相等的话就匹配成功。
对于支持设备树的 Linux 版本号,一般设备驱动为了兼容性都支持设备树和无设备树两种
匹配方式。也就是第一种匹配方式一般都会存在,第三种和第四种只要存在一种就可以,一般
用的最多的还是第四种,也就是直接比较驱动和设备的 name 字段,毕竟这种方式最简单了。
3.实验:
实验编写两个模块程序然后注册到platform总线上,驱动模块(platform_driver.c)和设备模块(platform_device.c),设备模块和驱动模块加载成功以后就会进行匹配,匹配成功后驱动模块中的 probe 函数就会执行,probe 函数中就是传统的字符设备驱动那一套。
3.1.device驱动代码:
Makefile:
#!/bin/bash
# 定义工具链路径变量
TOOLCHAIN_PATH="/samba2/home/liukuangjie/work/rk3588/rk3588-linux/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-"
export ARCH=arm64
export CROSS_COMPILE=${TOOLCHAIN_PATH}
obj-m += platform_device.o #此处要和你的驱动源文件同名
KDIR :=/samba2/home/liukuangjie/work/rk3588/rk3588-linux/kernel #这里是你的内核目录
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules #make操作
clean:
make -C $(KDIR) M=$(PWD) clean #make clean操作
platform_device:
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
static void neardi_platform_device_release(struct device *dev)
{
// 释放资源的回调函数
}
static struct platform_device neardi_platform_device = {
.name = "neardi_platform_device", // 设备名称
.dev.release = neardi_platform_device_release, // 释放资源的回调函数
};
static int __init neardi_platform_device_init(void)
{
int ret;
ret = platform_device_register(&neardi_platform_device); // 注册平台设备
if (ret) {
printk(KERN_ERR "Failed to register platform device\n");
return ret;
}
printk(KERN_INFO "Platform device registered\n");
return 0;
}
static void __exit neardi_platform_device_exit(void)
{
platform_device_unregister(&neardi_platform_device); // 注销平台设备
printk(KERN_INFO "Platform device unregistered\n");
}
module_init(neardi_platform_device_init);
module_exit(neardi_platform_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("neardi");
3.2. driver驱动代码
Makefile
#!/bin/bash
# 定义工具链路径变量
TOOLCHAIN_PATH="/samba2/home/liukuangjie/work/rk3588/rk3588-linux/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-"
export ARCH=arm64
export CROSS_COMPILE=${TOOLCHAIN_PATH}
obj-m += platform_driver.o #此处要和你的驱动源文件同名
KDIR :=/samba2/home/liukuangjie/work/rk3588/rk3588-linux/kernel #这里是你的内核目录
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules #make操作
clean:
make -C $(KDIR) M=$(PWD) clean #make clean操作
platform_driver.c
#include <linux/module.h>
#include <linux/platform_device.h>
// 平台设备的probe函数
static int neardi_platform_probe(struct platform_device *pdev)
{
printk(KERN_INFO "neardi_platform_probe: Probing platform device\n");
return 0;
}
// 平台设备的remove函数
static int neardi_platform_remove(struct platform_device *pdev)
{
printk(KERN_INFO "neardi_platform_remove: Removing platform device\n");
return 0;
}
// 平台驱动结构体定义
static struct platform_driver neardi_platform_driver = {
.probe = neardi_platform_probe,
.remove = neardi_platform_remove,
.driver = {
.name = "neardi_platform_device",
.owner = THIS_MODULE,
},
};
//初始化函数
static int __init neardi_platform_driver_init(void)
{
int ret;
// 注册平台驱动
ret = platform_driver_register(&neardi_platform_driver);
if (ret) {
printk(KERN_ERR "Failed to register platform driver\n");
return ret;
}
printk(KERN_INFO "neardi_platform_driver: Platform driver initialized\n");
return 0;
}
// 退出函数
static void __exit neardi_platform_driver_exit(void)
{
// 注销平台驱动
platform_driver_unregister(&neardi_platform_driver);
printk(KERN_INFO "neardi_platform_driver: Platform driver exited\n");
}
module_init(neardi_platform_driver_init);
module_exit(neardi_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("neardi");