Device 与 platform device的不同(一)

本文对比了Device与Platform Device的注册过程。两者都包含初始化和注册两个步骤,但具体实现不同。Device直接通过device_add()函数注册,而Platform Device还需额外注册资源,并通过platform_device_add()函数完成注册。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Device 与 platform device 在注册方法上有所不同。

Device 注册有两步,platform device注册也有两步,它们第一步相同,都是initialize设备,

但第二步有所不同,Device是直接调用device_add()函数来add设备,而platform device则调用platform_device_add()函数。

实际上,platform_device_add()里面也嵌套了device_add()函数(红色部分),不过在device_add()被调用之前,platform device要先注册它的resources。

就是下面蓝色的部分。

>>>>>>>>>>>>>>>>>>>> device的注册 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

void device_register(struct device *dev)

{

device_initialize(dev);

return device_add(dev);

}

>>>>>>>>>>>>>>>>>>>>>>device_register的第一步, 初始化device>>>>>>>>>>>>>>>>>>>>

void device_initialize(struct device *dev)

{

dev->kobj.kset = device_kset;

kboject_init(&dev->kobj, &device_ktype);

INIT_LIST_HEAD(&dev->dma_pools);

mutex_init(&dev->mutex);

lockdep_set_novalidate_class(&dev->mutex);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

device_pm_init(dev);

set_dev_node(dev, -1);

}

>>>>>>>>备注: struct device_private *p;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

struct device_private { //hold the private to the driver core portions of the device structure

struct klist klist_children; //containing all children of this device

struct klist_node knode_parent; //node in sibling list

struct klist_node knode_driver; //node in driver list

struct klist_node knode_bus; //node in bus list

void *driver_data; //private pointer for driver specific info.

struct device *device; //pointer back to the struct class that this structure is associated with

}; // Nothing outside of the driver core should ever touch these fields.

>>>>>>>>>>>>>>>>> device_register的第二步, 添加device>>>>>>>>>>>>>>>>>>>>>>>>>

int device_add(struct device *dev)

{

struct device *parent =NULL;

struct class_interface *class_intf;

int error = -EINVAL;

dev =get_device(dev);

if(!dev)

goto done;

if(!dev->p){

error = device_private_init(dev);

if(error)

goto done;

}

/* for statically allocated devices, which should all be converted some day, we need to initialize the name. We prevent reading back the name, and force the use of dev_name() */

if(dev->init_name){

dev_set_name(dev, "%s", dev->init_name);

dev->init_name =NULL;

}

if(!dev_name(dev)){

error = - EINVAL;

goto name_error;

}

pr_debug("device: '%s' : %s\n", dev_name(dev), __func__);

parent = getdevce(dev->parent);

setup_parent(dev, parent);

/*use paent numa_node*/

if(parent)

set_dev_node(dev, dev_to_node(parent));

//first, register with generic layer, we require the name to be set before, and pass NULL

error = kboject_add(&dev->kobj, dev->kobj.parent, NULL);

if(error)

goto Error;

//notify platform of device entry

if(platform_notify)

platform_notify(dev);

erro = device_create_file(dev, &uevent_attr);

if(error)

goto attrError;

if(MAJOR(dev->devt)){

error = device_create_file(dev, &devt_attr);

if(error)

goto ueventattrError;

error = device_create_sys_dev_entry(dev);

if(error)

goto devtattrError;

devtmpfs_create_node(dev);

}

error = device_add_class_symlinks(dev);

if(error)

goto SymlinkError;

error = device_add_attrs(dev);

if(error)

goto AttrsError;

error = bus_add_device(dev);

if(error)

goto BusError;

error = dpm_sysfs_add(dev);

if(error)

goto DPMError;

device_pm_add(dev);

/ /Notify clients of device addtion. This call must come after dpm_sysf_add() and before kobject_uevent().

if(dev->bus)

blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev);

kobject_uevent(&dev->kobj, KOBJ_ADD);

bus_probe_device(dev);

if(parent)

klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children);

if(dev->class){

mutex_lock(&dev->class->p->class_mutex);

//tie the class to the device

klist_add_tail(&dev->knode_class, &dev->class->p->class_devices);

list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node);

if(class_intf->add_dev)

class_intf->add_dev(dev, class_intf);

mutex_unlock(&dev->class->class_mutex);

}

done:

put_device(dev);

return error;

DPMError:

bus_remove_device(dev);

BusError:

device_remove_attrs(dev);

AttrsError:

device_remove_class_syslinks(dev);

SymlinkError:

if(MAJOR(dev->devt))

devtmpfs_delete_node(dev);

if(MAJOR(dev->devt))

device_remover_file(dev, &devt_attr);

ueventattrError:

device_remove_file(dev, &uevent_attr);

attrError:

kobject_uevent(&dev->kobj, KOBJ_REMOVE);

kobject_del(&dev->kobj);

Error:

cleanup_device_parent(dev);

if(parent)

put_device(parent);

name_error:

kfree(dev->p);

dev-> = NULL;

goto done;

}

>>>>>>>>>>>>>>> platform_device 的注册>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

// platform_device_register - add a platform-level device @pdev: platform device we're adding

int platform_device_register(struct platform_device *pdev)

{

device_initialize(&pdev->dev); //第一步与device完全相同

return platform_device_add(pdev);

}

>>>>>>>>>>>>>>>>>>>platform_device_add()>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

int platform_device_add(struct platform_device *pdev)

{

int i, ret = 0;

if(!pdev)

return -EINVAL;

if(!pdev->dev.parent)

pdev->dev.parent = &platform_bus;

pdev->dev.bus = &platform_bus_type;

if(pdev->id != -1)

dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);

else

dev_set_name(&pdev->dev, "%s", pdev->name);

for( i = -; i < pdev->num_resources; i++){

struct resource *p, *r = &pdev->resource[i];

if( r->name == NULL )

r->name = dev_name(&pdev->dev);

r->name = dev_name(&pdev->dev);

p = r->parent;

if(!p){

if(resource_type(r) == IORESOURCE_MEM)

p = &iomem_resource;

else if (resource_type(r) == IORESOURCE_IO)

p = &ioport_resource;

}

if( p && insert_resource(p, r)){

printk(KERN_ERR "%s: failed to claim resource %d\n", dev_name(&pdev->dev), i);

ret = -EBUSY;

goto failed;

}

}

pr_debug("Registering platform device '%s'. Parent at %s\n", dev_name(&pdev->dev), dev_name(pdev->dev.parent));

ret = device_add(&pdev->dev);

if(ret == 0)

return ret;

failed:

while( --i>=0){

struct resource *r = &pdev->resource[i];

unsigned long type = resouce_type(r);

if(type==IORESOURCE_MEM)||type==IORESOURCE_IO)

release_resource(r);

}

return ret;

}

}

<think>嗯,用户这次提出的问题涉及Linux驱动框架中platform_deviceplatform_driver的核心工作机制,看来是在深入理解平台设备驱动模型。用户已经看过我前面对module_platform_driver的解析,现在想进步厘清设备驱动两个结构体的关系。从用户引用资料来看,他对匹配机制特别关注,尤其是设备树出现后的变化。引用[3][4]明确指向platform_match函数,这个确实是关键。用户可能在实际开发中遇到了驱动匹配失败的问题,或者想设计自定义的设备-驱动绑定逻辑。注意到用户提供的引用片段存在几个技术要点需要串联:1.传统方案中platform_device需要显式注册(引用[1])2.匹配通过platform_bus_type.match函数实现(引用[3])3.设备树替代了platform_device的注册(引用[4])这些正好对应着驱动模型演进的三个阶段:裸设备注册、ACPI时代设备树时代。用户提到的“有些驱动里没有platform_device_register”特别值得展开,这正是设备树带来的变革。需要解释清楚:当采用设备树时,platform_device是由内核根据dtb自动生成的。可以画个对比图更直观:传统方式:驱动代码创建platform_device→注册→匹配→probe设备树方式:DTS→dtc→内核解析生成platform_device→匹配→probe关于匹配优先级(引用[2][3]提到platform_match源码),需要强调四步判断顺序:dt的compatible字符串最优先,然后是ACPIID,再是platformdevicename,最后是drv->id_table。这个顺序解释了为什么设备树现在成为主流方案。用户隐藏的问题可能是:“为什么我的驱动probe函数没执行?”这通常就是匹配环节失败。应该补充说明调试方法,比如检查/sys/bus/platform/devicesdrivers目录,以及如何通过dump_device()查看compatible字符串。</think> 在 Linux 驱动框架中,`platform_device` `platform_driver` 是平台总线模型的核心组件,它们通过以下方式协同工作: --- ### 、核心概念区别 | **组件** | **作用** | **生命周期** | **数据来源** | |--------------------|----------------------------------------------|-----------------------|--------------------------| | **`platform_device`** | 描述硬件资源<br>(如寄存器地址、中断号、时钟) | 设备注册时创建<br>系统启动或模块加载时生成 | 传统:代码显式定义<br>设备树:内核解析 DTS 自动生成[^4] | | **`platform_driver`** | 实现驱动逻辑<br>(如 `probe()`、`remove()`) | 驱动模块加载时注册<br>通过 `platform_driver_register()` | 由驱动开发者实现<br>需定义操作函数兼容性标识 | --- ### 二、关联机制:匹配流程 #### 1. **注册匹配过程** ```mermaid sequenceDiagram 设备注册->>总线: platform_device_register() 或 DTS 解析生成[^1][^4] 驱动注册->>总线: platform_driver_register() 总线-->>匹配函数: 调用 platform_match() alt 匹配成功 总线->>驱动: 执行 probe() 函数 else 匹配失败 总线-->>驱动: 等待下次匹配 end ``` #### 2. **匹配规则**(优先级从高到低)[^3][^4] 1. **设备树兼容性** ```c // 驱动定义兼容标识 static const struct of_device_id my_driver_ids[] = { { .compatible = "vendor,device-123" }, // 设备树中 compatible 匹配 {} }; ``` 2. **ACPI ID** (嵌入式系统较少用) 3. **平台设备名** ```c // platform_device.name 必须 platform_driver.driver.name 致 pdev.name = "my_device"; pdrv.driver.name = "my_device"; ``` 4. **驱动 ID 表** ```c static struct platform_device_id my_id_table[] = { {"device_v1", 0}, // 匹配 platform_device.id_entry {} }; ``` --- ### 三、工作流程对比 #### 1. **传统开发模式**(无设备树) ```c // 1. 显式定义设备资源 struct resource res[] = { [0] = DEFINE_RES_MEM(0x12345678, 0x100), // 寄存器地址 [1] = DEFINE_RES_IRQ(IRQ_NUM), // 中断号 }; // 2. 手动注册设备 struct platform_device pdev = { .name = "my_device", .resource = res, }; platform_device_register(&pdev); // 开发者主动注册[^1] // 3. 驱动通过 name 匹配 struct platform_driver pdrv = { .driver = { .name = "my_device" }, // 名称匹配关键 .probe = my_probe, }; platform_driver_register(&pdrv); ``` #### 2. **设备树模式**(主流) ```c // 设备树片段 (DTS) my_device: my_device@12345678 { compatible = "vendor,device-123"; // ⭐ 匹配标识 ⭐ reg = <0x12345678 0x100>; // 寄存器 interrupts = <0 10 IRQ_TYPE_EDGE_RISING>; // 中断 }; // 驱动仅注册 platform_driver struct platform_driver pdrv = { .driver = { .of_match_table = my_driver_ids, // 设备树兼容性表[^4] }, .probe = my_probe, }; module_platform_driver(pdrv); // 自动注册驱动 ``` - **优势**:硬件改动无需重编译驱动[^4] - **内核行为**: DTS → 内核解析 → 自动生成 `platform_device` → 匹配驱动 → 调用 `probe()` --- ### 四、关键函数交互 | **函数** | 作用对象 | 调用时机 | |------------------------|--------------------|--------------------------| | `platform_device_register()` | `platform_device` | 手动注册设备资源(传统模式) | | `platform_driver_register()` | `platform_driver` | 驱动模块初始化时 | | `platform_match()` (⭐总线内核函数⭐) | 总线仲裁 | 设备/驱动注册时自动触发[^3] | --- ### 五、典型问题解析 **问题:为什么某些驱动没有 `platform_device_register`?** **答案**:设备树系统中,设备由 DTS 描述,内核启动时**自动生成**对应的 `platform_device`[^4],驱动只需通过兼容性标识声明匹配关系即可。 **问题:probe 函数如何获取资源?** ```c static int my_probe(struct platform_device *pdev) { // 1. 设备树方式:通过 compatible 匹配后 struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); void __iomem *base = devm_ioremap_resource(&pdev->dev, mem); // 2. 传统方式:直接访问 pdev->resource // pdev->resource[0].start; // 物理地址 } ``` --- **附录:设备树到驱动的数据流** $$ \text{DTS 文件} \xrightarrow{\text{内核解析}} \text{platform\_device} \underset{\text{platform\_match}}{\overset{\text{匹配}}{\rightleftharpoons}} \text{platform\_driver} \xrightarrow{\text{probe()}} \text{驱动初始化} $$ - 设备树通过 `compatible` 提供**设备唯标识** - 驱动通过 `of_match_table` **声明能支持的设备**[^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值