问题描述
我有一个自定义的 linux 内核驱动程序,可以与旧的 ISA 卡(来自具有真正 ISA 总线的旧单处理器 PC)通信。我正在尝试将此驱动程序移植到配备 PCI-ISA 桥接器的新系统中。 旧驱动程序正在写入 I/O ISA 端口:
request_region(0x0280,8,"foo"); //0x0280 is a jumper-configured address in ISA card hardware.
//Then lots of:
outw_p(val,0x0280);
val = inw_p(0x0282);
... (ports in use 0x0280,0x0282 and 0x0284)
我尝试了相同的代码,但地址映射似乎不再起作用。区域请求不会出错,但我总是从所有 inw_p 读取中获得 65535(而在旧系统中,卡使用相同代码回答有意义的数据)。
我在此代码中找不到任何要编辑的内容以使其与桥一起工作。 我尝试将网桥作为 PCI 设备打开并通过以下方式获取其 I/O 端口地址:
dev = pci_get_device(vid,id,NULL); //Called with hardcoded bridge ids from lspci
result = pci_enable_device(dev); //dev not null,no errors
result = pci_request_regions(dev,"foo"); //No errors
value = pci_resource_start(dev,bar); //value is always 0 with any bar value
设备正在工作,因为我可以使用 pci_read_config_word 获取其供应商 ID,但我总是从任何 BAR 值中获取 0,而且 lspci -vvvv 没有给我地址/区域部分:
04:08.0 ISA bridge: Integrated Technology Express,Inc. IT8888F/G PCI to ISA Bridge with SMB [Golden Gate] (rev 03)
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- disINTx-
Status: Cap- 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
此外,BIOS 中似乎没有可用于此桥接器的配置选项。
在互联网上,我只找到了一些关于 PCI-ISA 桥的一般信息,所以我问:从自定义 linux 内核驱动程序成功与 PCI-ISA 桥后面的 ISA 卡通信所需的程序是什么?
解决方法
好的,经过大量的反复试验,我找到了问题所在。我会分享我的经验,希望有人觉得它有用(我的电脑有一块带有 IT8888F/G PCI-ISA 桥接器的 HD620-H81 主板)。
PCI-ISA 桥
我的 PCI-ISA 桥(可能还有许多其他桥)默认配置为“减法解码”pci 模式,这意味着“如果没有其他 pci 设备声明地址,则桥声明它”。
您可以通过运行 lspci -t
和 lscpi -vv
来检查减法解码模式,并且您应该看到通向 PCI-ISA 桥的树中的每个 PCIe-PCI/PCI-PCI 桥都配置为减法解码模式(ISA 桥本身不会显示为减法,因为从 PCI 的角度来看,ISA 桥只是一个 PCI 设备而不是桥)。
这意味着您没有为网桥分配 BAR,也不需要以任何方式直接与 pci 网桥设备交互。您可以直接访问您需要的绝对地址,桥接器将负责正确管理它(如果您对细节感兴趣,互联网上已经有很多信息可用于减法解码)。
总而言之:不需要对桥接器本身或 pci 设备采取任何措施,只需确保在 bios 中没有其他设备(例如串行或并行端口)被分配您感兴趣的端口/地址范围(如果是,请禁用它或更改其地址)。
I/O 端口
我发现我的 ISA 卡位于比它应该在的位置高 0x0800 端口的端口上(这对我来说是主要问题)。我不知道为什么,也许我的桥添加了一个固定的偏移量(如果你知道原因,可以在下面评论它!)。
我为找出正确地址所做的是运行一个函数,该函数将所有端口地址从 0x0100(跳过第一个最好不要写入随机数据的区域)迭代到 0xFFFF 并在每个端口地址运行卡检查例程直到它从卡片中找到我期望的正确答案。
int cardFound = 0;
int i;
for(i = 0x0100; i < 0xFFFF && !cardFound; i++)
{
if (request_region(i,SIZE) == NULL)
continue;
//Do card detection with ioport_map,iowrite16/ioread16,etc.
cardFound = do_custom_card_detection_procedure();
release_region(i,SIZE);
}
if (cardFound)
printk(KERN_INFO "Card found at port %d\n",i);
这种方法的缺点是你可能会通过在端口随机写入数据来做非常糟糕的事情,所以要小心,如果你真的想在做之前尝试检查 /proc/ioport 是否有坏的东西(我搞砸了渲染四分之一的屏幕,我不得不通过移除电源线和 CMOS 电池来完全重置电源以使其恢复正常:D)。