在嵌入式 Linux 驱动开发中,随着系统复杂度的增加,驱动的可重用性和可维护性变得尤为重要。为了应对这一挑战,Linux 内核提出了 驱动分离与分层 的设计思想,并在此基础上引入了 Platform 设备驱动模型。本文将深入探讨 Linux 驱动的分离与分层思想,以及 Platform 设备驱动模型的实现原理和开发方法。
1. 驱动分离与分层的思想
1.1 为什么需要驱动分离与分层?
在 Linux 内核中,驱动程序占据了大量的代码量。如果不对驱动程序进行有效的管理,代码的重复性和复杂性将迅速增加,导致内核变得臃肿且难以维护。为了解决这一问题,Linux 提出了 驱动分离与分层 的设计思想。
1.1.1 驱动分离
驱动分离的核心思想是将 设备驱动 和 主机驱动 分开。例如,对于 I2C 设备,I2C 控制器的主机驱动由芯片厂商提供,而设备驱动则由设备厂商提供。通过这种方式,设备驱动可以独立于具体的硬件平台,从而实现代码的重用。
1.1.2 驱动分层
驱动分层的核心思想是将驱动程序分为多个层次,每一层负责不同的功能。例如,输入子系统(Input Subsystem)将驱动程序分为 设备原始驱动层 和 核心层。设备原始驱动层负责与硬件交互,而核心层则负责处理输入事件的上报和处理。
1.2 驱动分离与分层的优势
- 代码重用性:通过分离设备驱动和主机驱动,可以减少代码的重复性。
- 可维护性:分层设计使得驱动程序的逻辑更加清晰,便于维护和扩展。
- 兼容性:设备驱动可以独立于具体的硬件平台,提高了驱动的兼容性。
2. Platform 设备驱动模型
在 Linux 内核中,有些外设并没有总线概念(如 GPIO、LED 等),但为了使用总线、驱动和设备模型,Linux 引入了 Platform 设备驱动模型。Platform 模型是一种虚拟总线模型,它包括以下三个核心组件:
- Platform 总线:负责管理 Platform 设备和 Platform 驱动的匹配。
- Platform 设备:描述设备的信息(如设备名称、资源等)。
- Platform 驱动:实现设备的驱动逻辑。
2.1 Platform 总线
Platform 总线是 Linux 内核中的一种虚拟总线,由 bus_type
结构体表示。其定义如下:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
2.1.1 Platform 总线的匹配机制
Platform 总线的核心是 match
函数,它负责匹配 Platform 设备和 Platform 驱动。platform_match
函数的定义如下:
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);
/* 设备树匹配 */
if (of_driver_match_device(dev, drv))
return 1;
/* ID 表匹配 */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* 名称匹配 */
return (strcmp(pdev->name, drv->name) == 0);
}
匹配机制分为以下几种:
- 设备树匹配:通过设备树的
compatible
属性进行匹配。 - ID 表匹配:通过
id_table
进行匹配。 - 名称匹配:通过设备名称和驱动名称进行匹配。
2.2 Platform 驱动
Platform 驱动由 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;
};
2.2.1 Platform 驱动的核心函数
- probe 函数:当驱动与设备匹配成功时,
probe
函数会被调用。通常在此函数中完成设备的初始化工作。 - remove 函数:当设备被移除时,
remove
函数会被调用。通常在此函数中完成资源的释放工作。
2.2.2 Platform 驱动的注册与注销
Platform 驱动的注册和注销通过以下函数完成:
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);
2.3 Platform 设备
Platform 设备由 platform_device
结构体表示,其定义如下:
struct platform_device {
const char *name;
int id;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
};
2.3.1 Platform 设备的核心成员
- name:设备名称,用于与驱动进行匹配。
- resource:设备资源(如寄存器地址、中断号等)。
- num_resources:资源数量。
2.3.2 Platform 设备的注册与注销
Platform 设备的注册和注销通过以下函数完成:
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
3. Platform 设备驱动的开发
3.1 设备树下的 Platform 驱动
在支持设备树的 Linux 内核中,设备信息通过设备树描述。Platform 驱动通过设备树的 compatible
属性与设备进行匹配。
3.1.1 设备树示例
&i2c1 {
mpu6050: mpu6050@68 {
compatible = "invensense,mpu6050";
reg = <0x68>;
};
};
3.1.2 Platform 驱动示例
static const struct of_device_id mpu6050_of_match[] = {
{ .compatible = "invensense,mpu6050" },
{ }
};
static struct platform_driver mpu6050_driver = {
.driver = {
.name = "mpu6050",
.of_match_table = mpu6050_of_match,
},
.probe = mpu6050_probe,
.remove = mpu6050_remove,
};
module_platform_driver(mpu6050_driver);
3.2 传统方式下的 Platform 驱动
在不支持设备树的 Linux 内核中,设备信息通过 platform_device
结构体描述。
3.2.1 Platform 设备示例
static struct resource mpu6050_resources[] = {
{
.start = 0x68,
.end = 0x68,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device mpu6050_device = {
.name = "mpu6050",
.id = -1,
.num_resources = ARRAY_SIZE(mpu6050_resources),
.resource = mpu6050_resources,
};
3.2.2 Platform 驱动示例
static struct platform_driver mpu6050_driver = {
.driver = {
.name = "mpu6050",
},
.probe = mpu6050_probe,
.remove = mpu6050_remove,
};
module_platform_driver(mpu6050_driver);
4. 总结
Linux 驱动的分离与分层思想是 Linux 内核设计中的重要理念,它通过将驱动分为多个层次和模块,提高了代码的重用性和可维护性。Platform 设备驱动模型是这一思想的典型应用,它通过虚拟总线的方式,实现了设备与驱动的分离。
在实际开发中,Platform 设备驱动模型广泛应用于各种外设驱动开发中。无论是设备树方式还是传统方式,Platform 模型都为驱动开发提供了统一的框架,极大地简化了驱动开发的复杂度。
通过本文的讲解,相信读者已经对 Linux 驱动的分离与分层思想以及 Platform 设备驱动模型有了深入的理解。在实际开发中,合理运用这些知识,可以帮助开发者编写出更加高效、稳定的驱动程序。