ISA 驱动程序¶
以下文本改编自 Rene Herman 编写的 ISA 总线驱动程序初始提交的提交消息。
在最近关于“使用平台设备的 ISA 驱动程序”的讨论中,有人指出 (ALSA) ISA 驱动程序遇到了一个问题,即由于 probe() 错误未通过驱动模型传递上来,导致无法在未找到其硬件时选择使驱动程序加载(更确切地说是设备注册)失败。在此过程中,我建议单独的 ISA 总线可能是最佳选择;Russell King 同意并建议该总线可以使用 .match() 方法进行实际的设备发现。
附件完成了这一点。对于这种旧的非(通用)可发现的 ISA 硬件,只有驱动程序本身可以进行发现,因此与 platform_bus 不同的是,这个 isa_bus 也将 match() 分配给驱动程序。
另一个区别是:这些设备仅在驱动模型中存在,因为驱动程序创建它们是为了驱动它们,这意味着所有设备创建也已内部化。
这种用法模型很好,并已获得 Takashi Iwai 和 Jaroslav Kysela 在 ALSA 方面的认可。现在 ALSA 驱动程序的 module_init(仅适用于旧版 ISA 驱动程序)变为
static int __init alsa_card_foo_init(void)
{
return isa_register_driver(&snd_foo_isa_driver, SNDRV_CARDS);
}
static void __exit alsa_card_foo_exit(void)
{
isa_unregister_driver(&snd_foo_isa_driver);
}
因此,它与其他总线模型非常相似。这消除了 ALSA ISA 驱动程序中大量的重复初始化代码。
传入的 isa_driver 结构体是一个常规的驱动程序结构体,它嵌入了一个 struct device_driver
,包含正常的 probe/remove/shutdown/suspend/resume 回调,以及前面提到的 .match 回调。
您看到的传入的“SNDRV_CARDS”是一个“unsigned int ndev”参数,指示要创建多少个设备并调用我们的方法。
platform_driver 回调以 platform_device 参数调用;而 isa_driver 回调直接以 struct device *dev, unsigned int id
对调用 —— 考虑到设备创建完全在总线内部,不通过传入 isa_dev 来泄露它们会更简洁。反正除了 struct device
之外,id 是我们唯一需要的东西,这也有助于在回调中编写更优雅的代码。
有了这个额外的 .match() 回调,ISA 驱动程序就拥有了所有选项。如果 ALSA 想保留旧的非加载行为,它可以将旧的 .probe 全部放在 .match 中,这样只有在一切都被发现存在并被确认后,它们才会保持注册。如果它想保留在转换为平台设备后无意中保持了一段时间的始终加载行为,它就可以不提供 .match(),而像以前一样在 .probe() 中完成所有操作。
如果它,正如 Takashi Iwai 之前所建议的,为了更紧密地遵循更合理的总线模型,希望在后续绑定可能成功时进行加载,那么它可以使用 .match() 来处理先决条件(例如检查用户是否希望启用该卡以及是否已传入端口/中断/DMA 值),并使用 .probe() 来处理其他所有事情。这是最好的模型。
关于代码...
这只导出了两个函数:isa_{,un}register_driver()。
isa_register_driver() 注册 struct device_driver
,然后循环遍历传入的 ndev,创建并注册设备。这会导致总线匹配方法对这些设备被调用,即
int isa_bus_match(struct device *dev, struct device_driver *driver)
{
struct isa_driver *isa_driver = to_isa_driver(driver);
if (dev->platform_data == isa_driver) {
if (!isa_driver->match ||
isa_driver->match(dev, to_isa_dev(dev)->id))
return 1;
dev->platform_data = NULL;
}
return 0;
}
它做的第一件事是检查这个设备是否确实是该驱动程序的一个设备,通过查看设备的 platform_data 指针是否设置为该驱动程序。平台设备比较字符串,但由于所有内容都是内部的,我们不需要这样做,因此 isa_register_driver() 滥用 dev->platform_data 作为 isa_driver 指针,我们可以在这里检查它。我相信 platform_data 可用于此目的,但如果不行,将 isa_driver 指针移动到私有结构体 isa_dev 中当然也可以。
然后,如果驱动程序没有提供 .match 方法,它就会匹配。如果提供了,则调用驱动程序的 match() 方法来确定匹配。
如果它**不**匹配,dev->platform_data 会被重置,以向 isa_register_driver 指示这一点,然后 isa_register_driver 可以再次注销该设备。
如果在所有这些过程中,有任何错误发生,或者根本没有设备匹配,则所有操作都会被回滚,并返回错误或 -ENODEV。
isa_unregister_driver() 只是注销匹配的设备和驱动程序本身。
module_isa_driver 是一个辅助宏,用于在模块初始化/退出时不执行任何特殊操作的 ISA 驱动程序。这消除了大量的样板代码。每个模块只能使用此宏一次,并且调用它会替换 module_init 和 module_exit。
max_num_isa_dev 是一个宏,用于根据 ISA 设备的地址范围,确定可以在 I/O 端口地址空间中注册的最大 ISA 设备数量。