linux 驱动的platform机制:
添加设备阶段:
设备注册的时候Platform_device_register()→platform_device_add()→(pdev→dev.bus = &platform_bus_type)→device_add(),就这样把设备给挂到虚拟的总线上
详细解析这个阶段流程:
在 Linux 中,platform
机制是一种用于管理无明确硬件总线关系的设备(通常是 SoC 上的设备)的框架。platform_device
是 Linux 内核中的一种设备类型,通常用于描述嵌入式系统中的设备。这些设备通常没有标准的硬件总线(如 PCI 或 USB),所以使用虚拟的 platform_bus
来管理。
以下详细解析 Platform_device_register()
添加设备阶段的流程:
1. Platform_device_register()
Platform_device_register()
是注册平台设备的函数,它的实现实际上会调用 platform_device_add()
:
C
int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); // 初始化设备结构体 return platform_device_add(pdev); // 添加设备到 platform 总线 }
关键步骤
调用
device_initialize(&pdev->dev)
:- 初始化设备结构体
pdev->dev
,这是一个通用的设备结构体(类型为struct device
)。 - 主要是准备设备的生命周期管理机制(如
kobject
的引用计数等)。
- 初始化设备结构体
调用
platform_device_add(pdev)
:- 将
pdev
添加到platform_bus
中。
- 将
2. platform_device_add()
核心功能是将 platform_device
添加到平台总线中。
函数实现如下:
C
int platform_device_add(struct platform_device *pdev) { int ret; if (!pdev) return -EINVAL; // 设置 platform_device 的总线类型为 platform_bus pdev->dev.bus = &platform_bus_type; // 设置设备名字 if (!pdev->dev.release) { dev_set_name(&pdev->dev, "%s", pdev->name); } // 添加设备到设备模型 ret = device_add(&pdev->dev); // 关键步骤 if (ret == 0) platform_device_add_resources(pdev, pdev->resource, pdev->num_resources); return ret; }
关键步骤
检查
pdev
的有效性:- 确保
pdev
非空,否则返回错误。
- 确保
设置设备的总线类型:
- 将
pdev->dev.bus
设置为&platform_bus_type
,表示该设备属于platform_bus
类型。 platform_bus_type
是一个全局变量,定义了 platform 总线的行为。
C
struct bus_type platform_bus_type = { .name = "platform", .match = platform_match, // 用于设备与驱动匹配 };
- 将
设置设备名字:
- 如果设备没有定义
release
回调函数,则调用dev_set_name()
给设备设置一个名字(一般是pdev->name
)。
- 如果设备没有定义
调用
device_add(&pdev->dev)
:- 最关键的步骤:将设备添加到设备模型中,建立与内核设备模型的关联。
3. device_add()
device_add()
是设备模型的核心函数,用于将设备注册到内核的设备层。
函数实现如下(核心部分):
C
int device_add(struct device *dev) { int ret; // 检查设备相关字段是否有效 if (!dev->p || !dev->bus) return -EINVAL; // 设置设备的子系统层次关系 dev->parent = get_device(dev->parent); kobject_set_name(&dev->kobj, dev_name(dev)); // 添加设备到设备层 ret = kobject_add(&dev->kobj, dev->parent ? &dev->parent->kobj : NULL, dev_name(dev)); if (ret < 0) goto Error; // 设备和总线关联 ret = device_add_class_symlinks(dev); if (ret) goto Error; ret = bus_add_device(dev); if (ret) goto Error; // 通知驱动和用户层 ret = device_add_groups(dev, dev->groups); if (ret) goto Error; // 通知系统有新设备添加 kobject_uevent(&dev->kobj, KOBJ_ADD); return 0; Error: kobject_put(&dev->kobj); return ret; }
关键步骤
检查设备字段是否有效:
- 确保设备结构体中的必要字段(如
dev->p
和dev->bus
)已经初始化。
- 确保设备结构体中的必要字段(如
设置设备的层次关系:
- 如果设备有父设备,则设置父子关系。
- 通过
kobject_set_name()
给设备设置一个唯一的名字。
添加设备到设备模型:
- 调用
kobject_add()
将设备的 kobject 添加到内核的设备层次结构中。
- 调用
设备与总线关联:
- 调用
bus_add_device()
将设备添加到总线中。 - 对于平台设备,总线类型是
platform_bus_type
。
C
int bus_add_device(struct device *dev) { struct bus_type *bus = dev->bus; int error = 0; if (bus->p->drivers_autoprobe) { error = device_attach(dev); // 自动匹配驱动 } return error; }
- 调用
通知驱动和用户层:
- 调用
device_add_groups()
添加设备属性(如 sysfs 文件)。 - 调用
kobject_uevent()
发送KOBJ_ADD
事件通知用户层。
- 调用
4. 总线与设备的关联
当调用 device_add()
时,设备会被挂到总线 platform_bus
下:
pdev->dev.bus = &platform_bus_type
表示设备属于platform_bus
。device_add()
会调用bus_add_device()
将设备挂到总线。- 在
platform_bus
的结构中定义了match
函数,用于匹配设备和驱动。
总结流程
调用
platform_device_register()
:- 初始化设备结构体,调用
platform_device_add()
。
- 初始化设备结构体,调用
调用
platform_device_add()
:- 设置设备的总线类型为
platform_bus_type
。 - 调用
device_add()
将设备添加到内核设备模型。
- 设置设备的总线类型为
调用
device_add()
:- 将设备添加到内核的设备层次结构中。
- 与总线关联,触发设备与驱动的匹配流程。
- 发送
KOBJ_ADD
事件通知用户层。
通过这些步骤,设备最终被挂到虚拟的 platform_bus
总线上,并且可以被匹配到合适的驱动程序。