ISA 驱动¶
以下文本改编自 Rene Herman 编写的 ISA 总线驱动程序的初始提交消息。
在最近的“使用平台设备的 ISA 驱动程序”讨论中,有人指出 (ALSA) ISA 驱动程序遇到了以下问题:由于 probe() 错误没有通过驱动模型传递,因此无法在未找到硬件时选择驱动程序加载失败(设备注册)。在此过程中,我建议最好使用单独的 ISA 总线;Russell King 同意了,并建议该总线可以使用 .match() 方法进行实际的设备发现。
附件就是这样做的。对于这种旧的、非(通用)可发现的 ISA 硬件,只有驱动程序本身才能进行发现,因此与 platform_bus 不同,此 isa_bus 还会将 match() 分发到驱动程序。
另一个不同之处在于:这些设备仅因驱动程序创建它们而存在于驱动模型中,因为它可能想要驱动它们,这意味着所有设备创建也已内部化。
这种用法模型很好,并且已获得 ALSA 方面 Takashi Iwai 和 Jaroslav Kysela 的认可。ALSA 驱动程序 module_init 现在(对于仅限 oldisa 的驱动程序)变为
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 要干净得多。id 是我们除了 struct device
之外唯一想要的东西,并且它也使回调中的代码更简洁。
有了这个额外的 .match() 回调,ISA 驱动程序拥有所有选项。如果 ALSA 想要保留旧的非加载行为,它可以将所有旧的 .probe 放入 .match 中,这将仅在所有内容都存在并被考虑在内后才保持注册状态。如果它想要像在更改为平台设备后无意中做的一样始终加载的行为,它可以不提供 .match() 并像以前一样在 .probe() 中执行所有操作。
如果它像 Takashi Iwai 早些时候建议的那样,以更紧密地遵循更健全的总线模型的方式,想要在稍后的绑定可能成功时加载,它可以使用 .match() 来满足先决条件(例如检查用户是否想要启用该卡以及是否已传入端口/irq/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 指示这一点,然后它可以再次注销该设备。
如果在此过程中出现任何错误,或者根本没有设备匹配,则会再次回滚所有内容,并返回错误或 -ENODEV。
isa_unregister_driver() 只是注销匹配的设备和驱动程序本身。
module_isa_driver 是用于在模块 init/exit 中不做任何特殊操作的 ISA 驱动程序的辅助宏。这消除了很多样板代码。每个模块只能使用此宏一次,并且调用它会替换 module_init 和 module_exit。
max_num_isa_dev 是一个宏,用于确定在给定 ISA 设备地址范围的情况下,可以在 I/O 端口地址空间中注册的最大 ISA 设备数量。