内核添加dts后,device和device_driver的match匹配的变动:通过compatible属性进行匹配

内核中DTS匹配机制
本文探讨了内核在引入DTS后,对于device和device_driver匹配机制的变化,特别是平台总线和I2C总线中如何通过OpenFirmware和ACPI样式匹配设备与驱动。重点介绍了__of_match_node()函数的作用以及of_device_id结构体数组和device_node结构体的兼容性匹配过程。

转载地址:

内核添加dts后,device和device_driver的match匹配的变动:

先看platform总线:

/driver/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);

/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))//通过这个OpenFirmware的匹配方式(of就是OpenFirmware的缩写)
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这个函数最终调用到__of_match_node()函数,在这个函数里,通过把device_driver的of_match_table(of_device_id结构体的数组)和device里的of_node(device_node结构体)进行匹配,匹配方式是分别比较两者的name、type、和compatible字符串,三者要同时相同(一般name、和type为空,只比较compatible字符串,比较compatible时,是比较整个字符串,不管字符串里的逗号的,直接compare整个字符串。所以,可以看出,对dts的OpenFirmware device的支持是从最根本的driver-model上支持的,在device和device_driver两者上都加了of_device的成员,也在bus_type的match函数里添加了of_device的匹配语句。所以driver-model的device、device_driver、bus_type都加了支持。

现在内核解析dtb文件,然后创建platform设备时,大部分platform设备是没有名字的,我还纠结那它们怎么和驱动match,原来bus_type的match函数添加了of_driver_match_device()这个匹配方式。他们大部分是通过compatible这个属性匹配成功的(这个compatible也对应dts里的compatible字符串)


再来看下其他总线的match函数,比如i2c的,也有of_device的匹配方式:

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client*client = i2c_verify_client(dev);
struct i2c_driver*driver;

if (!client)
return 0;

/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;


/* Then ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;

driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;

return 0;
}

`i2c_device_id` 能够与设备树节点的 `compatible` 属性匹配,是因为 **I2C 子系统通过 `of_match_table` `i2c_device_id` 的联合机制**,实现了对设备树非设备树场景的兼容支持。这种设计允许驱动同时适配传统 I2C 设备(通过 `i2c_device_id` 匹配设备树描述的 I2C 设备(通过 `compatible` 匹配)。以下是详细原理实现细节: --- ### **1. 核心机制:I2C 驱动的双重匹配支持** #### **(1) 传统匹配:`i2c_device_id`** - **作用**: 在非设备树场景下,I2C 设备通过 **I2C 适配器名称 + 设备地址** 注册为 `i2c_client`,驱动通过 `i2c_device_id` 列表匹配设备的 **名称(`name`)**。 - **关键数据结构**: ```c struct i2c_device_id { char name[I2C_NAME_SIZE]; // 设备名称(如"max77802") kernel_ulong_t driver_data; // 私有数据(传递给probe) }; ``` - **匹配逻辑**: 驱动注册时提供 `i2c_device_id` 数组,内核比较设备的 `name` 与数组中的条目: ```c static const struct i2c_device_id my_id_table[] = { { "max77802", 0x1234 }, // 匹配设备名"max77802" { }, }; static struct i2c_driver my_driver = { .driver = { .name = "my-driver", }, .id_table = my_id_table, // 传统匹配表 .probe = my_probe, }; ``` #### **(2) 设备树匹配:`of_match_table`** - **作用**: 在设备树场景下,I2C 设备通过 `compatible` 属性描述硬件兼容性,驱动通过 `of_match_table` 匹配属性。 - **关键数据结构**: ```c static const struct of_device_id my_of_match[] = { { .compatible = "vendor,max77802" }, // 匹配设备树的compatible { }, }; ``` - **绑定到驱动**: 在 `i2c_driver` 中通过 `of_match_table` 字段关联: ```c static struct i2c_driver my_driver = { .driver = { .name = "my-driver", .of_match_table = of_match_ptr(my_of_match), // 设备树匹配 }, .id_table = my_id_table, // 传统匹配 .probe = my_probe, }; ``` --- ### **2. 为什么 `i2c_device_id` 能匹配 `compatible`?** #### **(1) 间接匹配:通过设备树到 `i2c_client` 的转换** - **设备树节点的 `compatible` 触发 `i2c_client` 创建**: 内核解析设备树时,若节点包含 `compatible` 属性,且匹配到 I2C 驱动的 `of_match_table`,会为该节点创建 `i2c_client`,并设置 `client->name` 为 `compatible` 的第一个值(或驱动指定的名称)。 ```dts i2c@12340000 { compatible = "vendor,max77802"; reg = <0x38>; // I2C设备地址 }; ``` 生成的 `i2c_client` 的 `name` 字段默认为 `"max77802"`(取自 `compatible`)。 - **`i2c_device_id` 匹配 `client->name`**: 驱动的 `i2c_device_id` 表通过 `name` 字段匹配 `client->name`,从而间接实现 `compatible` 到 `i2c_device_id` 的关联。 #### **(2) 匹配优先级与逻辑** 1. **设备树优先**: - 内核首先尝试通过 `of_match_table` 匹配驱动。 - 匹配成功后,若驱动同时提供 `i2c_device_id`,会进一步检查 `i2c_device_id` 是否包含与 `client->name` 匹配的条目(**非必须**)。 2. **传统匹配备用**: - 若设备树未匹配成功(或未使用设备树),内核遍历驱动的 `i2c_device_id`,匹配已注册的 `i2c_client` 的 `name` 字段。 --- ### **3. 实际匹配流程示例** #### **(1) 设备树节点** ```dts i2c { #address-cells = <1>; #size-cells = <0>; pmic: pmic@38 { compatible = "ti,tps65910"; reg = <0x38>; // I2C设备地址 }; }; ``` #### **(2) 驱动定义** ```c static const struct i2c_device_id tps65910_id[] = { { "tps65910", 0 }, // 传统匹配:name="tps65910" { }, }; static const struct of_device_id tps65910_of_match[] = { { .compatible = "ti,tps65910" }, // 设备树匹配compatible="ti,tps65910" { }, }; static struct i2c_driver tps65910_driver = { .driver = { .name = "tps65910", .of_match_table = of_match_ptr(tps65910_of_match), }, .id_table = tps65910_id, .probe = tps65910_probe, }; ``` #### **(3) 匹配过程** 1. **设备树解析**: - 内核解析设备树,发现节点 `compatible = "ti,tps65910"`。 - 调用 `of_platform_populate()` 创建 `i2c_client`,并设置 `client->name = "tps65910"`(来自 `compatible` 的第一个值)。 2. **驱动匹配**: - 调用 `i2c_register_driver()` 注册驱动时,内核优先尝试设备树匹配: - 遍历驱动的 `of_match_table`,发现 `compatible = "ti,tps65910"` 匹配,记录匹配结果。 - 若设备树匹配成功,进一步检查 `i2c_device_id`(可选): - 遍历 `id_table`,发现 `i2c_device_id.name = "tps65910"` 匹配 `client->name`。 3. **调用 `probe()`**: - 匹配成功后,调用 `tps65910_probe()`,并传递 `i2c_client` 对象。 --- ### **4. 关键验证与调试** #### **(1) 检查设备树匹配** ```bash dmesg | grep "of_driver_match_device" ``` 输出示例: ``` [ 1.234567] tps65910: matched device 'tps65910' with compatible 'ti,tps65910' ``` #### **(2) 确认 `i2c_client` 名称** ```bash ls /sys/bus/i2c/devices/0-0038/ # 0-0038为I2C总线地址 cat /sys/bus/i2c/devices/0-0038/name # 确认设备名(应与i2c_device_id一致) ``` #### **(3) 调试匹配顺序** - 在驱动的 `probe()` 中打印匹配信息: ```c static int tps65910_probe(struct i2c_client *client, const struct i2c_device_id *id) { pr_info("Probed device: %s, addr=0x%02x\n", client->name, client->addr); if (id) { pr_info("Matched ID: %s, data=0x%lx\n", id->name, id->driver_data); } // ... } ``` --- ### **5. 常见问题与解决** #### **(1) 驱动未绑定但设备树节点存在** - **原因**: - `compatible` 字符串拼写错误。 - 驱动未正确注册 `of_match_table`。 - I2C 适配器未初始化。 - **解决**: - 检查设备树节点的 `compatible` 属性与驱动的 `of_match_table` 是否一致。 - 确认 I2C 适配器已注册(`ls /sys/bus/i2c/devices/`)。 #### **(2) 传统匹配失效** - **原因**: - 设备未通过板级文件显式注册为 `i2c_client`。 - `i2c_device_id` 的 `name` 字段与设备实际名称不匹配。 - **解决**: - 检查设备是否通过 `i2c_new_device()` 显式注册。 - 确认 `i2c_device_id` 的 `name` 字段与设备名称一致。 #### **(3) 混合匹配冲突** - **场景**: 驱动同时支持设备树传统匹配,但设备树节点未正确转换。 - **解决**: - 确保设备树节点包含 `compatible` 属性。 - 在板级初始化中调用 `of_platform_populate()` 手动触发转换。 --- ### **6. 总结** - **`i2c_device_id` 与 `compatible` 的关联**: I2C 子系统通过以下方式实现兼容: 1. 设备树节点的 `compatible` 属性被转换为 `i2c_client` 的 `name` 字段。 2. 驱动的 `i2c_device_id` 通过 `name` 匹配设备,而 `of_match_table` 通过 `compatible` 匹配设备树节点。 3. 内核优先尝试设备树匹配(`of_match_table`),失败后再尝试传统匹配(`i2c_device_id`)。 - **设计优势**: - **兼容性**:支持新旧硬件。 - **灵活性**:允许驱动同时适配多种硬件描述方式。 - **可维护性**:通过统一机制管理匹配逻辑。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值