probe的调用

probe的调用
最近看到linux的设备驱动模型,关于Kobject、Kset等还不是很清淅。看到了struct device_driver这个结构时,想到一个问题:它的初始化函数到底在哪里调用呢?以前搞PCI驱动时用pci驱动注册函数就可以调用它,搞 s3c2410驱动时只要在mach-smdk2410.c中的struct platform_device *smdk2410_devices {}中加入设备也会调用。但从来就没有想过具体的驱动注册并调用probe的过程。

于是打开SourceInsight追踪了一下:

从driver_register看起:
int driver_register(struct device_driver * drv)
{
klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
init_completion(&drv->unloaded);
return bus_add_driver(drv);
}

klist_init与init_completion没去管它,可能是2.6的这个设备模型要做的一些工作。直觉告诉我要去bus_add_driver。

bus_add_driver中:
都是些Kobject 与 klist 、attr等。还是与设备模型有关的。但是其中有一句:
driver_attach(drv);
单听名字就很像:
void driver_attach(struct device_driver * drv)
{
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
这个熟悉,遍历总线上的设备并设用__driver_attach。
在__driver_attach中又主要是这样:
driver_probe_device(drv, dev);
跑到driver_probe_device中去看看:
有一段很重要:
if (drv->bus->match && !drv->bus->match(dev, drv))
goto Done;
明显,是调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。
继承执行的话:
if (drv->probe) {
ret = drv->probe(dev);
if (ret) {
dev->driver = NULL;
goto ProbeFailed;
}
只要probe存在则调用之。至此就完成了probe的调用。

这个过程链的关键还是在drv->bus->match ,因为其余的地方出错的话就是注册失败,而只要注册不失败且match返回1,那么就铁定会调用驱程的probe了。你可以注册一个总线类型和总线,并在 match中总是返回 1, 会发现,只要struct device_driver中的bus类型正确时,probe函数总是被调用.
PCI设备有自己的总线模型,估计在它的match中就有一个判断的条件。
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *pci_drv = to_pci_driver(drv);
const struct pci_device_id *found_id;

found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;

return 0;
}
再往下跟踪就知道主要是根据我们熟悉的id_table来的。

原文地址 http://blog.youkuaiyun.com/shawnrong/archive/2007/11/23/1899391.aspx

另外从别处追加以下:
有两个重要的链表挂在bus上,一个是设备device链表,一个是驱动driver链表。

每当我们向一根bus注册一个驱动driver时,套路是这样的:

driver_register(struct device_driver * drv) -> bus_add_driver() -> driver_attach() ->

bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

bus_for_each_dev遍历该总线上所有的device,执行一次__driver_attach(),看能不能将驱动关联(attach)到某个设备上去。

__driver_attach()

->driver_probe_device()

->drv->bus->match(dev, drv), // 调用bus的match函数,看device和driver匹不匹配。如果匹配上,

继续执行really_probe()。

->really_probe()

->driver->probe()。(如果bus->probe非空,则调用bus->probe)

而每当我们向一根bus添加一个硬件时时,套路是这样的:

device_add()

\\ device_add 中有很多操作kobject,注册sysfs,形成硬件hiberarchy结构的代码。

如果您忘记了,先回头去参考参考"我是sysfs"

->bus_attach_device() -> device_attach() ->bus_for_each_drv()

bus_for_each_drv与bus_for_each_dev类似,遍历该总线上所有的driver,执行一次__device_attach(),看能不能将设备关联(attach)到某个已登记的驱动上去。

__device_attach()

->driver_probe_device() //后面与上面一样

总结一些,一句话,注册一个某个bus的驱动就是先把驱动自己链入到bus驱动链表中去,在从bus的设备链表中一一寻找,看有没有自己可以关联上的设备。找到就probe,再把二者bind起来。反之,添加设备道理也是一样的。

最近看到linux的设备驱动模型,关于Kobject、Kset等还不是很清淅。看到了struct device_driver这个结构时,想到一个问题:它的初始化函数到底在哪里调用呢?以前搞PCI驱动时用pci驱动注册函数就可以调用它,搞 s3c2410驱动时只要在mach-smdk2410.c中的struct platform_device *smdk2410_devices {}中加入设备也会调用。但从来就没有想过具体的驱动注册并调用probe的过程。

于是打开SourceInsight追踪了一下:

从driver_register看起:
int driver_register(struct device_driver * drv)
{
klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
init_completion(&drv->unloaded);
return bus_add_driver(drv);
}

klist_init与init_completion没去管它,可能是2.6的这个设备模型要做的一些工作。直觉告诉我要去bus_add_driver。

bus_add_driver中:
都是些Kobject 与 klist 、attr等。还是与设备模型有关的。但是其中有一句:
driver_attach(drv);
单听名字就很像:
void driver_attach(struct device_driver * drv)
{
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
这个熟悉,遍历总线上的设备并设用__driver_attach。
在__driver_attach中又主要是这样:
driver_probe_device(drv, dev);
跑到driver_probe_device中去看看:
有一段很重要:
if (drv->bus->match && !drv->bus->match(dev, drv))
goto Done;
明显,是调用的驱动的总线上的match函数。如果返回1,则可以继续,否则就Done了。
继承执行的话:
if (drv->probe) {
ret = drv->probe(dev);
if (ret) {
dev->driver = NULL;
goto ProbeFailed;
}
只要probe存在则调用之。至此就完成了probe的调用。

这个过程链的关键还是在drv->bus->match ,因为其余的地方出错的话就是注册失败,而只要注册不失败且match返回1,那么就铁定会调用驱程的probe了。你可以注册一个总线类型和总线,并在 match中总是返回 1, 会发现,只要struct device_driver中的bus类型正确时,probe函数总是被调用.
PCI设备有自己的总线模型,估计在它的match中就有一个判断的条件。
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *pci_drv = to_pci_driver(drv);
const struct pci_device_id *found_id;

found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;

return 0;
}
再往下跟踪就知道主要是根据我们熟悉的id_table来的。

原文地址 http://blog.youkuaiyun.com/shawnrong/archive/2007/11/23/1899391.aspx

另外从别处追加以下:
有两个重要的链表挂在bus上,一个是设备device链表,一个是驱动driver链表。

每当我们向一根bus注册一个驱动driver时,套路是这样的:

driver_register(struct device_driver * drv) -> bus_add_driver() -> driver_attach() ->

bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

bus_for_each_dev遍历该总线上所有的device,执行一次__driver_attach(),看能不能将驱动关联(attach)到某个设备上去。

__driver_attach()

->driver_probe_device()

->drv->bus->match(dev, drv), // 调用bus的match函数,看device和driver匹不匹配。如果匹配上,

继续执行really_probe()。

->really_probe()

->driver->probe()。(如果bus->probe非空,则调用bus->probe)

而每当我们向一根bus添加一个硬件时时,套路是这样的:

device_add()

\\ device_add 中有很多操作kobject,注册sysfs,形成硬件hiberarchy结构的代码。

如果您忘记了,先回头去参考参考"我是sysfs"

->bus_attach_device() -> device_attach() ->bus_for_each_drv()

bus_for_each_drv与bus_for_each_dev类似,遍历该总线上所有的driver,执行一次__device_attach(),看能不能将设备关联(attach)到某个已登记的驱动上去。

__device_attach()

->driver_probe_device() //后面与上面一样

总结一些,一句话,注册一个某个bus的驱动就是先把驱动自己链入到bus驱动链表中去,在从bus的设备链表中一一寻找,看有没有自己可以关联上的设备。找到就probe,再把二者bind起来。反之,添加设备道理也是一样的。

<think> 我们正在讨论Linux内核中的驱动probe机制。用户之前询问了时钟控制器的`recalc_rate`回调配置,现在转向了另一个驱动函数`fsl_lpspi_probe`的调用时机。根据用户提供的引用内容,特别是引用[1]中关于`really_probe`函数的描述,我们可以解释probe函数的调用流程。 用户引用的内容说明了设备驱动匹配后的probe调用过程: 1. 首先,设备所属总线的probe函数被调用(如果存在)。 2. 如果总线没有probe函数,则调用驱动自己的probe函数。 因此,`fsl_lpspi_probe`作为SPI控制器的驱动probe函数,其调用时机是在设备与驱动匹配之后,由内核的驱动模型通过`really_probe`函数调用的。 具体步骤: - 当内核启动或动态加载驱动时,会进行设备与驱动的匹配。 - 匹配成功后(通过compatible字符串匹配),内核会调用`really_probe`函数。 - 在`really_probe`中,会依次调用总线的probe(如果有)和驱动的probe(即`fsl_lpspi_probe`)。 所以,`fsl_lpspi_probe`的调用时机是:当设备树中描述的SPI控制器节点与驱动中定义的`of_device_id`表项匹配成功时,在内核初始化或模块加载过程中由驱动核心调用。 此外,用户提供的引用[3]中提到了SPI传输函数,这进一步说明我们讨论的是SPI控制器驱动,其中`fsl_lpspi_probe`是NXP i.MX系列芯片的LPSPI(Low Power SPI)控制器的驱动探测函数。 总结调用时机: 1. 内核启动过程中,设备与驱动匹配时。 2. 动态加载驱动模块时,如果设备存在且匹配。 3. 热插拔事件(如果支持)导致设备出现时。 在设备树中,SPI控制器节点会有一个compatible属性,例如: ```dts lpspi0: spi@40394000 { compatible = "fsl,imx7ulp-lpspi"; reg = <0x40394000 0x1000>; interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clk IMX7ULP_CLK_DUMMY>, <&clk IMX7ULP_CLK_LPSPI0>; clock-names = "ipg", "per"; dmas = <&edma0 0 4>, <&edma0 0 3>; dma-names = "rx","tx"; status = "disabled"; }; ``` 驱动中会有对应的`of_device_id`表: ```c static const struct of_device_id fsl_lpspi_dt_ids[] = { { .compatible = "fsl,imx7ulp-lpspi", .data = &imx7ulp_lpspi_devtype, }, { /* sentinel */ } }; ``` 当两者匹配时,`fsl_lpspi_probe`被调用。 相关问题: 1. 设备树中如何配置SPI控制器节点? 2. 如何编写SPI控制器驱动的probe函数? 3. 内核驱动匹配的详细流程是怎样的? </think> ### `fsl_lpspi_probe` 函数的调用时机分析 `fsl_lpspi_probe` 是 NXP i.MX 系列芯片的 LPSPI(Low Power SPI)控制器驱动中的探测函数。其调用时机遵循 Linux 内核标准的驱动探测机制,发生在以下关键节点: #### 1. **驱动与设备匹配阶段** 当内核检测到与驱动兼容的设备时触发: - **设备树匹配**:内核解析设备树(DTS)时发现匹配节点: ```dts lpspi0: spi@40394000 { compatible = "fsl,imx7ulp-lpspi"; // 匹配驱动的compatible字段 reg = <0x40394000 0x1000>; interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clk IMX7ULP_CLK_DUMMY>; status = "okay"; }; ``` - **驱动注册时**:通过 `platform_driver_register()` 注册驱动: ```c static struct platform_driver fsl_lpspi_driver = { .probe = fsl_lpspi_probe, // 注册probe函数 .driver = { .name = "fsl_lpspi", .of_match_table = fsl_lpspi_dt_ids, // 包含compatible字段 }, }; module_platform_driver(fsl_lpspi_driver); ``` 当设备树的 `compatible` 与驱动的 `of_match_table` 匹配时,内核安排调用 `probe`[^1]。 #### 2. **内核探测执行流程** 匹配后内核执行探测链: ```mermaid graph LR A[设备树解析] --> B[创建platform_device] B --> C{驱动注册完成?} C -->|是| D[调用really_probe] D --> E[总线probe] --> F[驱动probe] F --> G[执行fsl_lpspi_probe] ``` 1. 内核调用 `really_probe()` 初始化设备 2. 先执行总线级回调(如 SPI 总线的 `probe`) 3. **最终调用驱动注册的 `fsl_lpspi_probe`**[^1] #### 3. **具体触发场景** | 场景 | 调用时机 | 触发条件 | |--------------------------|-----------------------------------|------------------------------| | 内核启动 | 初始化期间 | 设备树包含节点且 `status=okay` | | 模块动态加载 | `insmod` 或 `modprobe` 命令执行时 | 设备已存在于系统中 | | 热插拔(支持热插拔系统) | 设备插入时 | 支持动态设备管理的平台 | #### 4. **函数内部典型操作** `fsl_lpspi_probe` 主要完成硬件初始化: ```c static int fsl_lpspi_probe(struct platform_device *pdev) { // 1. 获取设备树资源 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); void __iomem *base = devm_ioremap_resource(&pdev->dev, res); // 2. 申请中断 irq = platform_get_irq(pdev, 0); devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, 0, dev_name(&pdev->dev), spi_controller); // 3. 初始化SPI控制器 spi_controller = spi_alloc_host(&pdev->dev, sizeof(struct fsl_lpspi_data)); spi_controller->setup = fsl_lpspi_setup; spi_controller->transfer_one = fsl_lpspi_transfer_one; // 数据传输函数[^3] // 4. 注册控制器 spi_register_controller(spi_controller); } ``` 此函数完成后,SPI 控制器即可响应数据传输请求(如调用 `transfer_one`)[^3]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值