sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/PCI/pcimodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/zh_TW/PCI/pcimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/it_IT/PCI/pcimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/ja_JP/PCI/pcimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/ko_KR/PCI/pcimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hPortuguese (Brazilian)}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/pt_BR/PCI/pcimodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/sp_SP/PCI/pcimodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhH/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/PCI/pci.rsthKubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh field_body)}(hDocumentation/PCI/pci.rst h]h)}(hDocumentation/PCI/pci.rsth]hDocumentation/PCI/pci.rst}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h翻译h]h翻译}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubj)}(h-司延腾 Yanteng Si h]h)}(h,司延腾 Yanteng Si h](h司延腾 Yanteng Si <}(hj4hhhNhNubh reference)}(hsiyanteng@loongson.cnh]hsiyanteng@loongson.cn}(hj>hhhNhNubah}(h]h ]h"]h$]h&]refurimailto:siyanteng@loongson.cnuh1j<hj4ubh>}(hj4hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj0ubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h校译h]h校译}(hjghhhNhNubah}(h]h ]h"]h$]h&]uh1hhjdhhhKubj)}(hhh]h}(h]h ]h"]h$]h&]uh1hhjdubeh}(h]h ]h"]h$]h&]uh1hhhhK hhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhtarget)}(h.. _cn_PCI_pci.rst:h]h}(h]h ]h"]h$]h&]refidcn-pci-pci-rstuh1jhKhhhhhhubhsection)}(hhh](htitle)}(h如何写Linux PCI驱动h]h如何写Linux PCI驱动}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(hhh]h)}(hhh](h)}(h作者h]h作者}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubj)}(hH- Martin Mares - Grant Grundler h]h bullet_list)}(hhh](h list_item)}(hMartin Mares h]h)}(hjh](hMartin Mares <}(hjhhhNhNubj=)}(h mj@ucw.czh]h mj@ucw.cz}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:mj@ucw.czuh1j<hjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h+Grant Grundler h]h)}(h*Grant Grundler h](hGrant Grundler <}(hjhhhNhNubj=)}(hgrundler@parisc-linux.orgh]hgrundler@parisc-linux.org}(hjhhhNhNubah}(h]h ]h"]h$]h&]refuri mailto:grundler@parisc-linux.orguh1j<hjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]bullet-uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubah}(h]h ]h"]h$]h&]uh1hhjhhhhhKubh)}(hXnPCI的世界是巨大的,而且充满了(大多数是不愉快的)惊喜。由于每个CPU架构实现了不同 的芯片组,并且PCI设备有不同的要求(呃,“特性”),结果是Linux内核中的PCI支持并不 像人们希望的那样简单。这篇短文试图向所有潜在的驱动程序作者介绍PCI设备驱动程序的 Linux APIs。h]hXnPCI的世界是巨大的,而且充满了(大多数是不愉快的)惊喜。由于每个CPU架构实现了不同 的芯片组,并且PCI设备有不同的要求(呃,“特性”),结果是Linux内核中的PCI支持并不 像人们希望的那样简单。这篇短文试图向所有潜在的驱动程序作者介绍PCI设备驱动程序的 Linux APIs。}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h更完整的资源是Jonathan Corbet、Alessandro Rubini和Greg Kroah-Hartman的 《Linux设备驱动程序》第三版。LDD3可以免费获得(在知识共享许可下),网址是: https://lwn.net/Kernel/LDD3/。h](h更完整的资源是Jonathan Corbet、Alessandro Rubini和Greg Kroah-Hartman的 《Linux设备驱动程序》第三版。LDD3可以免费获得(在知识共享许可下),网址是: }(hjKhhhNhNubj=)}(hhttps://lwn.net/Kernel/LDD3/h]hhttps://lwn.net/Kernel/LDD3/}(hjShhhNhNubah}(h]h ]h"]h$]h&]refurijUuh1j<hjKubh。}(hjKhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h然而,请记住,所有的文档都会受到“维护不及时”的影响。如果事情没有按照这里描述的那 样进行,请参考源代码。h]h然而,请记住,所有的文档都会受到“维护不及时”的影响。如果事情没有按照这里描述的那 样进行,请参考源代码。}(hjlhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK"hjhhubh)}(h}请将有关Linux PCI API的问题/评论/补丁发送到“Linux PCI” 邮件列表。h](hJ请将有关Linux PCI API的问题/评论/补丁发送到“Linux PCI” <}(hjzhhhNhNubj=)}(h"linux-pci@atrey.karlin.mff.cuni.czh]h"linux-pci@atrey.karlin.mff.cuni.cz}(hjhhhNhNubah}(h]h ]h"]h$]h&]refuri)mailto:linux-pci@atrey.karlin.mff.cuni.czuh1j<hjzubh> 邮件列表。}(hjzhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK%hjhhubj)}(hhh](j)}(hPCI驱动的结构体h]hPCI驱动的结构体}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhK*ubh)}(hPCI驱动通过pci_register_driver()在系统中“发现”PCI设备。实际上,它是反过来的。 当PCI通用代码发现一个新设备时,具有匹配“描述”的驱动程序将被通知。下面是这方面的细 节。h]hPCI驱动通过pci_register_driver()在系统中“发现”PCI设备。实际上,它是反过来的。 当PCI通用代码发现一个新设备时,具有匹配“描述”的驱动程序将被通知。下面是这方面的细 节。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK+hjhhubh)}(hX'pci_register_driver()将大部分探测设备的工作留给了PCI层,并支持设备的在线插入/移 除[从而在一个驱动中支持可热插拔的PCI、CardBus和Express-Card]。 pci_register_driver() 调用需要传入一个函数指针表,从而决定了驱动的高层结构体。h]hX'pci_register_driver()将大部分探测设备的工作留给了PCI层,并支持设备的在线插入/移 除[从而在一个驱动中支持可热插拔的PCI、CardBus和Express-Card]。 pci_register_driver() 调用需要传入一个函数指针表,从而决定了驱动的高层结构体。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK/hjhhubh)}(hf一旦驱动探测到一个PCI设备并取得了所有权,驱动通常需要执行以下初始化:h]hf一旦驱动探测到一个PCI设备并取得了所有权,驱动通常需要执行以下初始化:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK3hjhhubh block_quote)}(hXB- 启用设备 - 请求MMIO/IOP资源 - 设置DMA掩码大小(对于流式和一致的DMA) - 分配和初始化共享控制数据(pci_allocate_coherent()) - 访问设备配置空间(如果需要) - 注册IRQ处理程序(request_irq()) - 初始化非PCI(即芯片的LAN/SCSI/等部分) - 启用DMA/处理引擎 h]j)}(hhh](j)}(h 启用设备h]h)}(hjh]h 启用设备}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK5hjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h请求MMIO/IOP资源h]h)}(hjh]h请求MMIO/IOP资源}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK6hjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h6设置DMA掩码大小(对于流式和一致的DMA)h]h)}(hjh]h6设置DMA掩码大小(对于流式和一致的DMA)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK7hjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hA分配和初始化共享控制数据(pci_allocate_coherent())h]h)}(hj'h]hA分配和初始化共享控制数据(pci_allocate_coherent())}(hj)hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK8hj%ubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h&访问设备配置空间(如果需要)h]h)}(hj>h]h&访问设备配置空间(如果需要)}(hj@hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK9hj<ubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h$注册IRQ处理程序(request_irq())h]h)}(hjUh]h$注册IRQ处理程序(request_irq())}(hjWhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK:hjSubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h3初始化非PCI(即芯片的LAN/SCSI/等部分)h]h)}(hjlh]h3初始化非PCI(即芯片的LAN/SCSI/等部分)}(hjnhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK;hjjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h启用DMA/处理引擎 h]h)}(h启用DMA/处理引擎h]h启用DMA/处理引擎}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h- 禁用设备产生的IRQ - 释放IRQ(free_irq()) - 停止所有DMA活动 - 释放DMA缓冲区(包括一致性和数据流式) - 从其他子系统(例如scsi或netdev)上取消注册 - 释放MMIO/IOP资源 - 禁用设备 h]j)}(hhh](j)}(h禁用设备产生的IRQh]h)}(hjh]h禁用设备产生的IRQ}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK@hjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h释放IRQ(free_irq())h]h)}(hjh]h释放IRQ(free_irq())}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKAhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h停止所有DMA活动h]h)}(hjh]h停止所有DMA活动}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKBhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h6释放DMA缓冲区(包括一致性和数据流式)h]h)}(hjh]h6释放DMA缓冲区(包括一致性和数据流式)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKChjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h:从其他子系统(例如scsi或netdev)上取消注册h]h)}(hjh]h:从其他子系统(例如scsi或netdev)上取消注册}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKDhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h释放MMIO/IOP资源h]h)}(hj/h]h释放MMIO/IOP资源}(hj1hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKEhj-ubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h 禁用设备 h]h)}(h 禁用设备h]h 禁用设备}(hjHhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKFhjDubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]j)j*uh1jhhhK@hjubah}(h]h ]h"]h$]h&]uh1jhhhK@hjhhubh)}(hr这些主题中的大部分都在下面的章节中有所涉及。其余的内容请参考LDD3或 。h]hr这些主题中的大部分都在下面的章节中有所涉及。其余的内容请参考LDD3或 。}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKHhjhhubh)}(h如果没有配置PCI子系统(没有设置 ``CONFIG_PCI`` ),下面描述的大多数PCI函数被定 义为内联函数,要么完全为空,要么只是返回一个适当的错误代码,以避免在驱动程序中出现 大量的 ``ifdef`` 。h](h.如果没有配置PCI子系统(没有设置 }(hjvhhhNhNubhliteral)}(h``CONFIG_PCI``h]h CONFIG_PCI}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjvubh ),下面描述的大多数PCI函数被定 义为内联函数,要么完全为空,要么只是返回一个适当的错误代码,以避免在驱动程序中出现 大量的 }(hjvhhhNhNubj)}(h ``ifdef``h]hifdef}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjvubh 。}(hjvhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKJhjhhubeh}(h]pciah ]h"]pci驱动的结构体ah$]h&]uh1jhjhhhhhK*ubj)}(hhh](j)}(h调用pci_register_driver()h]h调用pci_register_driver()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKPubh)}(hPCI设备驱动程序在初始化过程中调用 ``pci_register_driver()`` ,并提供一个指向 描述驱动程序的结构体的指针( ``struct pci_driver`` ):h](h1PCI设备驱动程序在初始化过程中调用 }(hjhhhNhNubj)}(h``pci_register_driver()``h]hpci_register_driver()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubhE ,并提供一个指向 描述驱动程序的结构体的指针( }(hjhhhNhNubj)}(h``struct pci_driver``h]hstruct pci_driver}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh ):}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKRhjhhubh)}(h该API在以下内核代码中:h]h该API在以下内核代码中:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKUhjhhubh)}(hinclude/linux/pci.h pci_driverh]hinclude/linux/pci.h pci_driver}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKWhjhhubh)}(hID表是一个由 ``struct pci_device_id`` 结构体成员组成的数组,以一个全零的成员 结束。一般来说,带有静态常数的定义是首选。h](hID表是一个由 }(hjhhhNhNubj)}(h``struct pci_device_id``h]hstruct pci_device_id}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubhz 结构体成员组成的数组,以一个全零的成员 结束。一般来说,带有静态常数的定义是首选。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKZhjhhubh)}(h该API在以下内核代码中:h]h该API在以下内核代码中:}(hj1hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK]hjhhubh)}(h-include/linux/mod_devicetable.h pci_device_idh]h-include/linux/mod_devicetable.h pci_device_id}(hj?hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK_hjhhubh)}(hn大多数驱动程序只需要 ``PCI_DEVICE()`` 或 ``PCI_DEVICE_CLASS()`` 来设置一个 pci_device_id表。h](h大多数驱动程序只需要 }(hjMhhhNhNubj)}(h``PCI_DEVICE()``h]h PCI_DEVICE()}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjMubh 或 }(hjMhhhNhNubj)}(h``PCI_DEVICE_CLASS()``h]hPCI_DEVICE_CLASS()}(hjghhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjMubh$ 来设置一个 pci_device_id表。}(hjMhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKbhjhhubh)}(hc新的 ``PCI ID`` 可以在运行时被添加到设备驱动的 ``pci_ids`` 表中,如下所示::h](h新的 }(hjhhhNhNubj)}(h ``PCI ID``h]hPCI ID}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh/ 可以在运行时被添加到设备驱动的 }(hjhhhNhNubj)}(h ``pci_ids``h]hpci_ids}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh 表中,如下所示:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKehjhhubh literal_block)}(hnecho "vendor device subvendor subdevice class class_mask driver_data" > \ /sys/bus/pci/drivers/{driver}/new_idh]hnecho "vendor device subvendor subdevice class class_mask driver_data" > \ /sys/bus/pci/drivers/{driver}/new_id}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKghjhhubh)}(h所有字段都以十六进制值传递(没有前置0x)。供应商和设备字段是强制性的,其他字段是可 选的。用户只需要传递必要的可选字段:h]h所有字段都以十六进制值传递(没有前置0x)。供应商和设备字段是强制性的,其他字段是可 选的。用户只需要传递必要的可选字段:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKjhjhhubj)}(h- subvendor和subdevice字段默认为PCI_ANY_ID (FFFFFFF)。 - class和classmask字段默认为0 - driver_data默认为0UL。 - override_only字段默认为0。 h]j)}(hhh](j)}(h;subvendor和subdevice字段默认为PCI_ANY_ID (FFFFFFF)。h]h)}(hjh]h;subvendor和subdevice字段默认为PCI_ANY_ID (FFFFFFF)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKmhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h!class和classmask字段默认为0h]h)}(hjh]h!class和classmask字段默认为0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKnhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hdriver_data默认为0UL。h]h)}(hjh]hdriver_data默认为0UL。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKohjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h!override_only字段默认为0。 h]h)}(h override_only字段默认为0。h]h override_only字段默认为0。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKphjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]j)j*uh1jhhhKmhjubah}(h]h ]h"]h$]h&]uh1jhhhKmhjhhubh)}(h请注意, ``driver_data`` 必须与驱动程序中定义的任何一个 ``pci_device_id`` 条 目所使用的值相匹配。如果所有的 ``pci_device_id`` 成员都有一个非零的driver_data 值,这使得driver_data字段是强制性的。h](h 请注意, }(hj?hhhNhNubj)}(h``driver_data``h]h driver_data}(hjGhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj?ubh/ 必须与驱动程序中定义的任何一个 }(hj?hhhNhNubj)}(h``pci_device_id``h]h pci_device_id}(hjYhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj?ubh3 条 目所使用的值相匹配。如果所有的 }(hj?hhhNhNubj)}(h``pci_device_id``h]h pci_device_id}(hjkhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj?ubhZ 成员都有一个非零的driver_data 值,这使得driver_data字段是强制性的。}(hj?hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKrhjhhubh)}(h一旦添加,驱动程序探测程序将被调用,以探测其(新更新的) ``pci_ids`` 列表中列出的 任何无人认领的PCI设备。h](hU一旦添加,驱动程序探测程序将被调用,以探测其(新更新的) }(hjhhhNhNubj)}(h ``pci_ids``h]hpci_ids}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh5 列表中列出的 任何无人认领的PCI设备。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKvhjhhubh)}(h当驱动退出时,它只是调用 ``pci_unregister_driver()`` ,PCI层会自动调用驱动处理 的所有设备的移除钩子。h](h%当驱动退出时,它只是调用 }(hjhhhNhNubj)}(h``pci_unregister_driver()``h]hpci_unregister_driver()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubhG ,PCI层会自动调用驱动处理 的所有设备的移除钩子。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKyhjhhubj)}(hhh](j)}(h(驱动程序功能/数据的“属性”h]h(驱动程序功能/数据的“属性”}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhK~ubh)}(hb请在适当的地方标记初始化和清理函数(相应的宏在中定义):h]hb请在适当的地方标记初始化和清理函数(相应的宏在中定义):}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hX====== ============================================== __init 初始化代码。在驱动程序初始化后被抛弃。 __exit 退出代码。对于非模块化的驱动程序来说是忽略的。 ====== ============================================== h]htable)}(hhh]htgroup)}(hhh](hcolspec)}(hhh]h}(h]h ]h"]h$]h&]colwidthKuh1jhjubj)}(hhh]h}(h]h ]h"]h$]h&]colwidthK.uh1jhjubhtbody)}(hhh](hrow)}(hhh](hentry)}(hhh]h)}(h__inith]h__init}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhj ubj)}(hhh]h)}(h9初始化代码。在驱动程序初始化后被抛弃。h]h9初始化代码。在驱动程序初始化后被抛弃。}(hj,hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj)ubah}(h]h ]h"]h$]h&]uh1jhj ubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(h__exith]h__exit}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjIubah}(h]h ]h"]h$]h&]uh1jhjFubj)}(hhh]h)}(hE退出代码。对于非模块化的驱动程序来说是忽略的。h]hE退出代码。对于非模块化的驱动程序来说是忽略的。}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj`ubah}(h]h ]h"]h$]h&]uh1jhjFubeh}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]colsKuh1jhjubah}(h]h ]h"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]uh1jhhhKhjhhubh)}(h1关于何时/何地使用上述属性的提示:h]h1关于何时/何地使用上述属性的提示:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hX,- module_init()/module_exit()函数(以及所有仅由这些函数调用的初始化函数)应该被标记 - 为__init/__exit。 - 不要标记pci_driver结构体。 - 如果你不确定应该使用哪种标记,请不要标记一个函数。不标记函数比标记错误的函数更好。 h]j)}(hhh](j)}(hmmodule_init()/module_exit()函数(以及所有仅由这些函数调用的初始化函数)应该被标记 h]h)}(hlmodule_init()/module_exit()函数(以及所有仅由这些函数调用的初始化函数)应该被标记h]hlmodule_init()/module_exit()函数(以及所有仅由这些函数调用的初始化函数)应该被标记}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h为__init/__exit。 h]h)}(h为__init/__exit。h]h为__init/__exit。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h#不要标记pci_driver结构体。 h]h)}(h"不要标记pci_driver结构体。h]h"不要标记pci_driver结构体。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h}如果你不确定应该使用哪种标记,请不要标记一个函数。不标记函数比标记错误的函数更好。 h]h)}(h{如果你不确定应该使用哪种标记,请不要标记一个函数。不标记函数比标记错误的函数更好。h]h{如果你不确定应该使用哪种标记,请不要标记一个函数。不标记函数比标记错误的函数更好。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]j)j*uh1jhhhKhjubah}(h]h ]h"]h$]h&]uh1jhhhKhjhhubeh}(h]id1ah ]h"](驱动程序功能/数据的“属性”ah$]h&]uh1jhjhhhhhK~ubeh}(h]pci-register-driverah ]h"]调用pci_register_driver()ah$]h&]uh1jhjhhhhhKPubj)}(hhh](j)}(h如何手动搜索PCI设备h]h如何手动搜索PCI设备}(hj*hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj'hhhhhKubh)}(hX PCI驱动最好有一个非常好的理由不使用 ``pci_register_driver()`` 接口来搜索PCI设备。 PCI设备被多个驱动程序控制的主要原因是一个PCI设备实现了几个不同的HW服务。例如,组合的 串行/并行端口/软盘控制器。h](h4PCI驱动最好有一个非常好的理由不使用 }(hj8hhhNhNubj)}(h``pci_register_driver()``h]hpci_register_driver()}(hj@hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj8ubh 接口来搜索PCI设备。 PCI设备被多个驱动程序控制的主要原因是一个PCI设备实现了几个不同的HW服务。例如,组合的 串行/并行端口/软盘控制器。}(hj8hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubh)}(h0可以使用以下结构体进行手动搜索:h]h0可以使用以下结构体进行手动搜索:}(hjXhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubh)}(h(通过供应商和设备ID进行搜索::h]h'通过供应商和设备ID进行搜索:}(hjfhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubj)}(hrstruct pci_dev *dev = NULL; while (dev = pci_get_device(VENDOR_ID, DEVICE_ID, dev)) configure_device(dev);h]hrstruct pci_dev *dev = NULL; while (dev = pci_get_device(VENDOR_ID, DEVICE_ID, dev)) configure_device(dev);}hjtsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj'hhubh)}(h1按类别ID搜索(以类似的方式迭代)::h]h0按类别ID搜索(以类似的方式迭代):}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubj)}(hpci_get_class(CLASS_ID, dev)h]hpci_get_class(CLASS_ID, dev)}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj'hhubh)}(hB通过供应商/设备和子系统供应商/设备ID进行搜索::h]hA通过供应商/设备和子系统供应商/设备ID进行搜索:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubj)}(hMpci_get_subsys(VENDOR_ID,DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev).h]hMpci_get_subsys(VENDOR_ID,DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev).}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj'hhubh)}(h你可以使用常数 ``PCI_ANY_ID`` 作为 ``VENDOR_ID`` 或 ``DEVICE_ID`` 的通 配符替代。例如,这允许搜索来自一个特定供应商的任何设备。h](h你可以使用常数 }(hjhhhNhNubj)}(h``PCI_ANY_ID``h]h PCI_ANY_ID}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh 作为 }(hjhhhNhNubj)}(h ``VENDOR_ID``h]h VENDOR_ID}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh 或 }(hjhhhNhNubj)}(h ``DEVICE_ID``h]h DEVICE_ID}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh\ 的通 配符替代。例如,这允许搜索来自一个特定供应商的任何设备。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubh)}(h这些函数是热拔插安全的。它们会增加它们所返回的 ``pci_dev`` 的参考计数。你最终 必须通过调用 ``pci_dev_put()`` 来减少这些设备上的参考计数(可能在模块卸载时)。h](hF这些函数是热拔插安全的。它们会增加它们所返回的 }(hjhhhNhNubj)}(h ``pci_dev``h]hpci_dev}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh0 的参考计数。你最终 必须通过调用 }(hjhhhNhNubj)}(h``pci_dev_put()``h]h pci_dev_put()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubhI 来减少这些设备上的参考计数(可能在模块卸载时)。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubeh}(h]id2ah ]h"]如何手动搜索pci设备ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h设备初始化步骤h]h设备初始化步骤}(hj; hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj8 hhhhhKubh)}(hZ正如介绍中所指出的,大多数PCI驱动需要以下步骤进行设备初始化:h]hZ正如介绍中所指出的,大多数PCI驱动需要以下步骤进行设备初始化:}(hjI hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj8 hhubj)}(hXG- 启用设备 - 请求MMIO/IOP资源 - 设置DMA掩码大小(对于流式和一致的DMA) - 分配和初始化共享控制数据(pci_allocate_coherent()) - 访问设备配置空间(如果需要) - 注册IRQ处理程序(request_irq()) - 初始化non-PCI(即芯片的LAN/SCSI/等部分) - 启用DMA/处理引擎 h]j)}(hhh](j)}(h 启用设备h]h)}(hj` h]h 启用设备}(hjb hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj^ ubah}(h]h ]h"]h$]h&]uh1jhj[ ubj)}(h请求MMIO/IOP资源h]h)}(hjw h]h请求MMIO/IOP资源}(hjy hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhju ubah}(h]h ]h"]h$]h&]uh1jhj[ ubj)}(h6设置DMA掩码大小(对于流式和一致的DMA)h]h)}(hj h]h6设置DMA掩码大小(对于流式和一致的DMA)}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhj[ ubj)}(hA分配和初始化共享控制数据(pci_allocate_coherent())h]h)}(hj h]hA分配和初始化共享控制数据(pci_allocate_coherent())}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhj[ ubj)}(h&访问设备配置空间(如果需要)h]h)}(hj h]h&访问设备配置空间(如果需要)}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhj[ ubj)}(h(注册IRQ处理程序(request_irq())h]h)}(hj h]h(注册IRQ处理程序(request_irq())}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhj[ ubj)}(h4初始化non-PCI(即芯片的LAN/SCSI/等部分)h]h)}(hj h]h4初始化non-PCI(即芯片的LAN/SCSI/等部分)}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhj[ ubj)}(h启用DMA/处理引擎 h]h)}(h启用DMA/处理引擎h]h启用DMA/处理引擎}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhj[ ubeh}(h]h ]h"]h$]h&]j)j*uh1jhhhKhjW ubah}(h]h ]h"]h$]h&]uh1jhhhKhj8 hhubh)}(h驱动程序可以在任何时候访问PCI配置空间寄存器。(嗯,几乎如此。当运行BIST时,配置 空间可以消失......但这只会导致PCI总线主控中止,读取配置将返回垃圾值)。)h]h驱动程序可以在任何时候访问PCI配置空间寄存器。(嗯,几乎如此。当运行BIST时,配置 空间可以消失......但这只会导致PCI总线主控中止,读取配置将返回垃圾值)。)}(hj# hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj8 hhubj)}(hhh](j)}(h启用PCI设备h]h启用PCI设备}(hj4 hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj1 hhhhhKubh)}(hx在接触任何设备寄存器之前,驱动程序需要通过调用 ``pci_enable_device()`` 启用 PCI设备。这将:h](hF在接触任何设备寄存器之前,驱动程序需要通过调用 }(hjB hhhNhNubj)}(h``pci_enable_device()``h]hpci_enable_device()}(hjJ hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjB ubh 启用 PCI设备。这将:}(hjB hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj1 hhubj)}(h- 唤醒处于暂停状态的设备。 - 分配设备的I/O和内存区域(如果BIOS没有这样做)。 - 分配一个IRQ(如果BIOS没有)。 h]j)}(hhh](j)}(h$唤醒处于暂停状态的设备。h]h)}(hjk h]h$唤醒处于暂停状态的设备。}(hjm hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhji ubah}(h]h ]h"]h$]h&]uh1jhjf ubj)}(hC分配设备的I/O和内存区域(如果BIOS没有这样做)。h]h)}(hj h]hC分配设备的I/O和内存区域(如果BIOS没有这样做)。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhjf ubj)}(h)分配一个IRQ(如果BIOS没有)。 h]h)}(h(分配一个IRQ(如果BIOS没有)。h]h(分配一个IRQ(如果BIOS没有)。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1jhjf ubeh}(h]h ]h"]h$]h&]j)j*uh1jhhhKhjb ubah}(h]h ]h"]h$]h&]uh1jhhhKhj1 hhubh)}(h5pci_enable_device() 可能失败,检查返回值。h]h)}(hj h]h5pci_enable_device() 可能失败,检查返回值。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1hhj1 hhhhhNubhwarning)}(hXOS BUG:在启用这些资源之前,我们没有检查资源分配情况。如果我们在调用 之前调用pci_request_resources(),这个顺序会更合理。目前,当两个设备被分配 了相同的范围时,设备驱动无法检测到这个错误。这不是一个常见的问题,不太可能很快 得到修复。 这个问题之前已经讨论过了,但从2.6.19开始没有改变: https://lore.kernel.org/r/20060302180025.GC28895@flint.arm.linux.org.uk/h](h)}(hXLOS BUG:在启用这些资源之前,我们没有检查资源分配情况。如果我们在调用 之前调用pci_request_resources(),这个顺序会更合理。目前,当两个设备被分配 了相同的范围时,设备驱动无法检测到这个错误。这不是一个常见的问题,不太可能很快 得到修复。h]hXLOS BUG:在启用这些资源之前,我们没有检查资源分配情况。如果我们在调用 之前调用pci_request_resources(),这个顺序会更合理。目前,当两个设备被分配 了相同的范围时,设备驱动无法检测到这个错误。这不是一个常见的问题,不太可能很快 得到修复。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubh)}(h这个问题之前已经讨论过了,但从2.6.19开始没有改变: https://lore.kernel.org/r/20060302180025.GC28895@flint.arm.linux.org.uk/h](hI这个问题之前已经讨论过了,但从2.6.19开始没有改变: }(hj hhhNhNubj=)}(hHhttps://lore.kernel.org/r/20060302180025.GC28895@flint.arm.linux.org.uk/h]hHhttps://lore.kernel.org/r/20060302180025.GC28895@flint.arm.linux.org.uk/}(hj hhhNhNubah}(h]h ]h"]h$]h&]refurij uh1j<hj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhj ubeh}(h]h ]h"]h$]h&]uh1j hj1 hhhhhNubh)}(hpci_set_master()将通过设置PCI_COMMAND寄存器中的总线主控位来启用DMA。 ``pci_clear_master()`` 将通过清除总线主控位来禁用DMA,它还修复了延迟计时器的 值,如果它被BIOS设置成假的。h](hXpci_set_master()将通过设置PCI_COMMAND寄存器中的总线主控位来启用DMA。 }(hj hhhNhNubj)}(h``pci_clear_master()``h]hpci_clear_master()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubhx 将通过清除总线主控位来禁用DMA,它还修复了延迟计时器的 值,如果它被BIOS设置成假的。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj1 hhubh)}(hX如果PCI设备可以使用 ``PCI Memory-Write-Invalidate`` 事务,请调用 ``pci_set_mwi()`` 。 这将启用 ``Mem-Wr-Inval`` 的 ``PCI_COMMAND`` 位,也确保缓存行大小寄存器被正确设置。检 查 ``pci_set_mwi()`` 的返回值,因为不是所有的架构或芯片组都支持 ``Memory-Write-Invalidate`` 。 另外,如果 ``Mem-Wr-Inval`` 是好的,但不是必须的,可以调用 ``pci_try_set_mwi()`` ,让 系统尽最大努力来启用 ``Mem-Wr-Inval`` 。h](h如果PCI设备可以使用 }(hj) hhhNhNubj)}(h``PCI Memory-Write-Invalidate``h]hPCI Memory-Write-Invalidate}(hj1 hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubh 事务,请调用 }(hj) hhhNhNubj)}(h``pci_set_mwi()``h]h pci_set_mwi()}(hjC hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubh 。 这将启用 }(hj) hhhNhNubj)}(h``Mem-Wr-Inval``h]h Mem-Wr-Inval}(hjU hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubh 的 }(hj) hhhNhNubj)}(h``PCI_COMMAND``h]h PCI_COMMAND}(hjg hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubhB 位,也确保缓存行大小寄存器被正确设置。检 查 }(hj) hhhNhNubj)}(h``pci_set_mwi()``h]h pci_set_mwi()}(hjy hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubhA 的返回值,因为不是所有的架构或芯片组都支持 }(hj) hhhNhNubj)}(h``Memory-Write-Invalidate``h]hMemory-Write-Invalidate}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubh 。 另外,如果 }(hj) hhhNhNubj)}(h``Mem-Wr-Inval``h]h Mem-Wr-Inval}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubh/ 是好的,但不是必须的,可以调用 }(hj) hhhNhNubj)}(h``pci_try_set_mwi()``h]hpci_try_set_mwi()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubh' ,让 系统尽最大努力来启用 }(hj) hhhNhNubj)}(h``Mem-Wr-Inval``h]h Mem-Wr-Inval}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj) ubh 。}(hj) hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj1 hhubeh}(h]id4ah ]h"]启用pci设备ah$]h&]uh1jhj8 hhhhhKubj)}(hhh](j)}(h请求MMIO/IOP资源h]h请求MMIO/IOP资源}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj hhhhhKubh)}(h内存(MMIO)和I/O端口地址不应该直接从PCI设备配置空间中读取。使用 ``pci_dev`` 结构体 中的值,因为PCI “总线地址”可能已经被arch/chip-set特定的内核支持重新映射为“主机物理” 地址。h](h\内存(MMIO)和I/O端口地址不应该直接从PCI设备配置空间中读取。使用 }(hj hhhNhNubj)}(h ``pci_dev``h]hpci_dev}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubh 结构体 中的值,因为PCI “总线地址”可能已经被arch/chip-set特定的内核支持重新映射为“主机物理” 地址。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(hL参见io_mapping函数,了解如何访问设备寄存器或设备内存。h]hL参见io_mapping函数,了解如何访问设备寄存器或设备内存。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(hX%设备驱动需要调用 ``pci_request_region()`` 来确认没有其他设备已经在使用相同的地址 资源。反之,驱动应该在调用 ``pci_disable_device()`` 之后调用 ``pci_release_region()`` 。 这个想法是为了防止两个设备在同一地址范围内发生冲突。h](h设备驱动需要调用 }(hj hhhNhNubj)}(h``pci_request_region()``h]hpci_request_region()}(hj( hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubhc 来确认没有其他设备已经在使用相同的地址 资源。反之,驱动应该在调用 }(hj hhhNhNubj)}(h``pci_disable_device()``h]hpci_disable_device()}(hj: hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubh 之后调用 }(hj hhhNhNubj)}(h``pci_release_region()``h]hpci_release_region()}(hjL hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubhS 。 这个想法是为了防止两个设备在同一地址范围内发生冲突。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubhtip)}(h见上面的操作系统BUG注释。目前(2.6.19),驱动程序只能在调用pci_enable_device() 后确定MMIO和IO端口资源的可用性。h]h)}(h见上面的操作系统BUG注释。目前(2.6.19),驱动程序只能在调用pci_enable_device() 后确定MMIO和IO端口资源的可用性。h]h见上面的操作系统BUG注释。目前(2.6.19),驱动程序只能在调用pci_enable_device() 后确定MMIO和IO端口资源的可用性。}(hjj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjf ubah}(h]h ]h"]h$]h&]uh1jd hj hhhhhNubh)}(h``pci_request_region()`` 的通用风格是 ``request_mem_region()`` (用于MMIO 范围)和 ``request_region()`` (用于IO端口范围)。对于那些不被 "正常 "PCI BAR描 述的地址资源,使用这些方法。h](j)}(h``pci_request_region()``h]hpci_request_region()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj~ ubh 的通用风格是 }(hj~ hhhNhNubj)}(h``request_mem_region()``h]hrequest_mem_region()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj~ ubh (用于MMIO 范围)和 }(hj~ hhhNhNubj)}(h``request_region()``h]hrequest_region()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj~ ubhs (用于IO端口范围)。对于那些不被 “正常 “PCI BAR描 述的地址资源,使用这些方法。}(hj~ hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(h9也请看下面的 ``pci_request_selected_regions()`` 。h](h也请看下面的 }(hj hhhNhNubj)}(h"``pci_request_selected_regions()``h]hpci_request_selected_regions()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubh 。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubeh}(h]mmio-iopah ]h"]请求mmio/iop资源ah$]h&]uh1jhj8 hhhhhKubj)}(hhh](j)}(h设置DMA掩码大小h]h设置DMA掩码大小}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj hhhhhKubh)}(h如果下面有什么不明白的地方,请参考使用通用设备的动态DMA映射。本节只是提醒大家, 驱动程序需要说明设备的DMA功能,并不是DMA接口的权威来源。h]h)}(h如果下面有什么不明白的地方,请参考使用通用设备的动态DMA映射。本节只是提醒大家, 驱动程序需要说明设备的DMA功能,并不是DMA接口的权威来源。h]h如果下面有什么不明白的地方,请参考使用通用设备的动态DMA映射。本节只是提醒大家, 驱动程序需要说明设备的DMA功能,并不是DMA接口的权威来源。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1hhj hhhhhNubh)}(hXn虽然所有的驱动程序都应该明确指出PCI总线主控的DMA功能(如32位或64位),但对于流式 数据来说,具有超过32位总线主站功能的设备需要驱动程序通过调用带有适当参数的 ``dma_set_mask()`` 来“注册”这种功能。一般来说,在系统RAM高于4G物理地址的情 况下,这允许更有效的DMA。h](h虽然所有的驱动程序都应该明确指出PCI总线主控的DMA功能(如32位或64位),但对于流式 数据来说,具有超过32位总线主站功能的设备需要驱动程序通过调用带有适当参数的 }(hj hhhNhNubj)}(h``dma_set_mask()``h]hdma_set_mask()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubhy 来“注册”这种功能。一般来说,在系统RAM高于4G物理地址的情 况下,这允许更有效的DMA。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(hq所有PCI-X和PCIe兼容设备的驱动程序必须调用 ``dma_set_mask()`` ,因为它们 是64位DMA设备。h](h:所有PCI-X和PCIe兼容设备的驱动程序必须调用 }(hj/ hhhNhNubj)}(h``dma_set_mask()``h]hdma_set_mask()}(hj7 hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj/ ubh% ,因为它们 是64位DMA设备。}(hj/ hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(hX同样,如果设备可以通过调用 ``dma_set_coherent_mask()`` 直接寻址到 4G物理地址以上的系统RAM中的“一致性内存”,那么驱动程序也必须“注册”这种功能。同 样,这包括所有PCI-X和PCIe兼容设备的驱动程序。许多64位“PCI”设备(在PCI-X之前) 和一些PCI-X设备对有效载荷(“流式”)数据具有64位DMA功能,但对控制(“一致性”)数 据则没有。h](h(同样,如果设备可以通过调用 }(hjO hhhNhNubj)}(h``dma_set_coherent_mask()``h]hdma_set_coherent_mask()}(hjW hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjO ubhX} 直接寻址到 4G物理地址以上的系统RAM中的“一致性内存”,那么驱动程序也必须“注册”这种功能。同 样,这包括所有PCI-X和PCIe兼容设备的驱动程序。许多64位“PCI”设备(在PCI-X之前) 和一些PCI-X设备对有效载荷(“流式”)数据具有64位DMA功能,但对控制(“一致性”)数 据则没有。}(hjO hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubeh}(h]dmaah ]h"]设置dma掩码大小ah$]h&]uh1jhj8 hhhhhKubj)}(hhh](j)}(h设置共享控制数据h]h设置共享控制数据}(hjz hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjw hhhhhMubh)}(h一旦DMA掩码设置完毕,驱动程序就可以分配“一致的”(又称共享的)内存。参见使用通 用设备的动态DMA映射,了解DMA API的完整描述。本节只是提醒大家,需要在设备上启 用DMA之前完成。h]h一旦DMA掩码设置完毕,驱动程序就可以分配“一致的”(又称共享的)内存。参见使用通 用设备的动态DMA映射,了解DMA API的完整描述。本节只是提醒大家,需要在设备上启 用DMA之前完成。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjw hhubeh}(h]id5ah ]h"]设置共享控制数据ah$]h&]uh1jhj8 hhhhhMubj)}(hhh](j)}(h初始化设备寄存器h]h初始化设备寄存器}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj hhhhhMubh)}(h一些驱动程序需要对特定的“功能”字段进行编程,或对其他“供应商专用”寄存器进行初始 化或重置。例如,清除挂起的中断。h]h一些驱动程序需要对特定的“功能”字段进行编程,或对其他“供应商专用”寄存器进行初始 化或重置。例如,清除挂起的中断。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj hhubeh}(h]id6ah ]h"]初始化设备寄存器ah$]h&]uh1jhj8 hhhhhMubj)}(hhh](j)}(h注册IRQ处理函数h]h注册IRQ处理函数}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj hhhhhMubh)}(h虽然调用 ``request_irq()`` 是这里描述的最后一步,但这往往只是初始化设备的另 一个中间步骤。这一步通常可以推迟到设备被打开使用时进行。h](h 虽然调用 }(hj hhhNhNubj)}(h``request_irq()``h]h request_irq()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubh 是这里描述的最后一步,但这往往只是初始化设备的另 一个中间步骤。这一步通常可以推迟到设备被打开使用时进行。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj hhubh)}(h所有IRQ线的中断处理程序都应该用 ``IRQF_SHARED`` 注册,并使用devid将IRQ映射 到设备(记住,所有的PCI IRQ线都可以共享)。h](h.所有IRQ线的中断处理程序都应该用 }(hj hhhNhNubj)}(h``IRQF_SHARED``h]h IRQF_SHARED}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj ubhb 注册,并使用devid将IRQ映射 到设备(记住,所有的PCI IRQ线都可以共享)。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM!hj hhubh)}(hX``request_irq()`` 将把一个中断处理程序和设备句柄与一个中断号联系起来。历史上, 中断号码代表从PCI设备到中断控制器的IRQ线。在MSI和MSI-X中(更多内容见下文),中 断号是CPU的一个“向量”。h](j)}(h``request_irq()``h]h request_irq()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh 将把一个中断处理程序和设备句柄与一个中断号联系起来。历史上, 中断号码代表从PCI设备到中断控制器的IRQ线。在MSI和MSI-X中(更多内容见下文),中 断号是CPU的一个“向量”。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM$hj hhubh)}(h``request_irq()`` 也启用中断。在注册中断处理程序之前,请确保设备是静止的,并且 没有任何中断等待。h](j)}(h``request_irq()``h]h request_irq()}(hj6hhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj2ubhw 也启用中断。在注册中断处理程序之前,请确保设备是静止的,并且 没有任何中断等待。}(hj2hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM(hj hhubh)}(hXMSI和MSI-X是PCI功能。两者都是“消息信号中断”,通过向本地APIC的DMA写入来向CPU发 送中断。MSI和MSI-X的根本区别在于如何分配多个“向量”。MSI需要连续的向量块,而 MSI-X可以分配几个单独的向量。h]hXMSI和MSI-X是PCI功能。两者都是“消息信号中断”,通过向本地APIC的DMA写入来向CPU发 送中断。MSI和MSI-X的根本区别在于如何分配多个“向量”。MSI需要连续的向量块,而 MSI-X可以分配几个单独的向量。}(hjNhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM+hj hhubh)}(hX在调用 ``request_irq()`` 之前,可以通过调用 ``pci_alloc_irq_vectors()`` 的PCI_IRQ_MSI和/或PCI_IRQ_MSIX标志来启用MSI功能。这将导致PCI支持将CPU向量数 据编程到PCI设备功能寄存器中。许多架构、芯片组或BIOS不支持MSI或MSI-X,调用 ``pci_alloc_irq_vectors`` 时只使用PCI_IRQ_MSI和PCI_IRQ_MSIX标志会失败, 所以尽量也要指定 ``PCI_IRQ_INTX`` 。h](h 在调用 }(hj\hhhNhNubj)}(h``request_irq()``h]h request_irq()}(hjdhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj\ubh 之前,可以通过调用 }(hj\hhhNhNubj)}(h``pci_alloc_irq_vectors()``h]hpci_alloc_irq_vectors()}(hjvhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj\ubh 的PCI_IRQ_MSI和/或PCI_IRQ_MSIX标志来启用MSI功能。这将导致PCI支持将CPU向量数 据编程到PCI设备功能寄存器中。许多架构、芯片组或BIOS不支持MSI或MSI-X,调用 }(hj\hhhNhNubj)}(h``pci_alloc_irq_vectors``h]hpci_alloc_irq_vectors}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj\ubhS 时只使用PCI_IRQ_MSI和PCI_IRQ_MSIX标志会失败, 所以尽量也要指定 }(hj\hhhNhNubj)}(h``PCI_IRQ_INTX``h]h PCI_IRQ_INTX}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hj\ubh 。}(hj\hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM/hj hhubh)}(h对MSI/MSI-X和传统INTx有不同中断处理程序的驱动程序应该在调用 ``pci_alloc_irq_vectors`` 后根据 ``pci_dev``结构体中的 ``msi_enabled`` 和 ``msix_enabled`` 标志选择正确的处理程序。h](hS对MSI/MSI-X和传统INTx有不同中断处理程序的驱动程序应该在调用 }(hjhhhNhNubj)}(h``pci_alloc_irq_vectors``h]hpci_alloc_irq_vectors}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh 后根据 }(hjhhhNhNubj)}(h*``pci_dev``结构体中的 ``msi_enabled``h]h&pci_dev``结构体中的 ``msi_enabled}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh 和 }(hjhhhNhNubj)}(h``msix_enabled``h]h msix_enabled}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh% 标志选择正确的处理程序。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM5hj hhubh)}(h3使用MSI有(至少)两个真正好的理由:h]h3使用MSI有(至少)两个真正好的理由:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM9hj hhubhenumerated_list)}(hhh](j)}(h根据定义,MSI是一个排他性的中断向量。这意味着中断处理程序不需要验证其设备是 否引起了中断。 h]h)}(h根据定义,MSI是一个排他性的中断向量。这意味着中断处理程序不需要验证其设备是 否引起了中断。h]h根据定义,MSI是一个排他性的中断向量。这意味着中断处理程序不需要验证其设备是 否引起了中断。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM;hj ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hMSI避免了DMA/IRQ竞争条件。到主机内存的DMA被保证在MSI交付时对主机CPU是可 见的。这对数据一致性和避 h]h)}(hMSI避免了DMA/IRQ竞争条件。到主机内存的DMA被保证在MSI交付时对主机CPU是可 见的。这对数据一致性和避h]hMSI避免了DMA/IRQ竞争条件。到主机内存的DMA被保证在MSI交付时对主机CPU是可 见的。这对数据一致性和避}(hj%hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM>hj!ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hh免控制数据过期都很重要。这个保证允许驱动程序省略MMIO读取,以刷新DMA流。 h]h)}(hg免控制数据过期都很重要。这个保证允许驱动程序省略MMIO读取,以刷新DMA流。h]hg免控制数据过期都很重要。这个保证允许驱动程序省略MMIO读取,以刷新DMA流。}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMAhj9ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]enumtypearabicprefixhsuffix)uh1jhj hhhhhM;ubh)}(hX参见drivers/infiniband/hw/mthca/或drivers/net/tg3.c了解MSI/MSI-X的使 用实例。h]hX参见drivers/infiniband/hw/mthca/或drivers/net/tg3.c了解MSI/MSI-X的使 用实例。}(hj\hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMChj hhubeh}(h]irqah ]h"]注册irq处理函数ah$]h&]uh1jhj8 hhhhhMubeh}(h]id3ah ]h"]设备初始化步骤ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(hPCI设备关闭h]hPCI设备关闭}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjzhhhhhMHubh)}(hO当一个PCI设备驱动程序被卸载时,需要执行以下大部分步骤:h]hO当一个PCI设备驱动程序被卸载时,需要执行以下大部分步骤:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMJhjzhhubj)}(hX - 禁用设备产生的IRQ - 释放IRQ(free_irq()) - 停止所有DMA活动 - 释放DMA缓冲区(包括流式和一致的) - 从其他子系统(例如scsi或netdev)上取消注册 - 禁用设备对MMIO/IO端口地址的响应 - 释放MMIO/IO端口资源 h]j)}(hhh](j)}(h禁用设备产生的IRQh]h)}(hjh]h禁用设备产生的IRQ}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMLhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h释放IRQ(free_irq())h]h)}(hjh]h释放IRQ(free_irq())}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h停止所有DMA活动h]h)}(hjh]h停止所有DMA活动}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMNhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h0释放DMA缓冲区(包括流式和一致的)h]h)}(hjh]h0释放DMA缓冲区(包括流式和一致的)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMOhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h:从其他子系统(例如scsi或netdev)上取消注册h]h)}(hjh]h:从其他子系统(例如scsi或netdev)上取消注册}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMPhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h+禁用设备对MMIO/IO端口地址的响应h]h)}(hjh]h+禁用设备对MMIO/IO端口地址的响应}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMQhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(h释放MMIO/IO端口资源 h]h)}(h释放MMIO/IO端口资源h]h释放MMIO/IO端口资源}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMRhj*ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]j)j*uh1jhhhMLhjubah}(h]h ]h"]h$]h&]uh1jhhhMLhjzhhubj)}(hhh](j)}(h停止设备上的IRQh]h停止设备上的IRQ}(hjQhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjNhhhhhMVubh)}(h如何做到这一点是针对芯片/设备的。如果不这样做,如果(也只有在)IRQ与另一个设备 共享,就会出现“尖叫中断”的可能性。h]h如何做到这一点是针对芯片/设备的。如果不这样做,如果(也只有在)IRQ与另一个设备 共享,就会出现“尖叫中断”的可能性。}(hj_hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMWhjNhhubh)}(hX当共享的IRQ处理程序被“解钩”时,使用同一IRQ线的其余设备仍然需要启用该IRQ。因此, 如果“脱钩”的设备断言IRQ线,假设它是其余设备中的一个断言IRQ线,系统将作出反应。 由于其他设备都不会处理这个IRQ,系统将“挂起”,直到它决定这个IRQ不会被处理并屏蔽 这个IRQ(100,000次之后)。一旦共享的IRQ被屏蔽,其余设备将停止正常工作。这不是 一个好事情。h]hX当共享的IRQ处理程序被“解钩”时,使用同一IRQ线的其余设备仍然需要启用该IRQ。因此, 如果“脱钩”的设备断言IRQ线,假设它是其余设备中的一个断言IRQ线,系统将作出反应。 由于其他设备都不会处理这个IRQ,系统将“挂起”,直到它决定这个IRQ不会被处理并屏蔽 这个IRQ(100,000次之后)。一旦共享的IRQ被屏蔽,其余设备将停止正常工作。这不是 一个好事情。}(hjmhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMZhjNhhubh)}(h这是使用MSI或MSI-X的另一个原因,如果它可用的话。MSI和MSI-X被定义为独占中断, 因此不容易受到“尖叫中断”问题的影响。h]h这是使用MSI或MSI-X的另一个原因,如果它可用的话。MSI和MSI-X被定义为独占中断, 因此不容易受到“尖叫中断”问题的影响。}(hj{hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM`hjNhhubeh}(h]id8ah ]h"]停止设备上的irqaph$]h&]uh1jhjzhhhhhMVubj)}(hhh](j)}(h 释放IRQh]h 释放IRQ}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMdubh)}(h一旦设备被静止(不再有IRQ),就可以调用free_irq()。这个函数将在任何待处理 的IRQ被处理后返回控制,从该IRQ上“解钩”驱动程序的IRQ处理程序,最后如果没有人 使用该IRQ,则释放它。h]h一旦设备被静止(不再有IRQ),就可以调用free_irq()。这个函数将在任何待处理 的IRQ被处理后返回控制,从该IRQ上“解钩”驱动程序的IRQ处理程序,最后如果没有人 使用该IRQ,则释放它。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMehjhhubeh}(h]id9ah ]h"] 释放irqah$]h&]uh1jhjzhhhhhMdubj)}(hhh](j)}(h停止所有DMA活动h]h停止所有DMA活动}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMkubh)}(h在试图取消分配DMA控制数据之前,停止所有的DMA操作是非常重要的。如果不这样做, 可能会导致内存损坏、挂起,在某些芯片组上还会导致硬崩溃。h]h在试图取消分配DMA控制数据之前,停止所有的DMA操作是非常重要的。如果不这样做, 可能会导致内存损坏、挂起,在某些芯片组上还会导致硬崩溃。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMlhjhhubh)}(hZ在停止IRQ后停止DMA可以避免IRQ处理程序可能重新启动DMA引擎的竞争。h]hZ在停止IRQ后停止DMA可以避免IRQ处理程序可能重新启动DMA引擎的竞争。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMohjhhubh)}(h虽然这个步骤听起来很明显,也很琐碎,但过去有几个“成熟”的驱动程序没有做好这个 步骤。h]h虽然这个步骤听起来很明显,也很琐碎,但过去有几个“成熟”的驱动程序没有做好这个 步骤。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMqhjhhubeh}(h]id10ah ]h"]停止所有dma活动ah$]h&]uh1jhjzhhhhhMkubj)}(hhh](j)}(h释放DMA缓冲区h]h释放DMA缓冲区}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMvubh)}(h一旦DMA被停止,首先要清理流式DMA。即取消数据缓冲区的映射,如果有的话,将缓 冲区返回给“上游”所有者。h]h一旦DMA被停止,首先要清理流式DMA。即取消数据缓冲区的映射,如果有的话,将缓 冲区返回给“上游”所有者。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMwhjhhubh)}(h<然后清理包含控制数据的“一致的”缓冲区。h]h<然后清理包含控制数据的“一致的”缓冲区。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMzhjhhubh)}(hR关于取消映射接口的细节,请参见Documentation/core-api/dma-api.rst。h]hR关于取消映射接口的细节,请参见Documentation/core-api/dma-api.rst。}(hj(hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM|hjhhubeh}(h]id11ah ]h"]释放dma缓冲区ah$]h&]uh1jhjzhhhhhMvubj)}(hhh](j)}(h从其他子系统取消注册h]h从其他子系统取消注册}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj>hhhhhMubh)}(hXA大多数低级别的PCI设备驱动程序支持其他一些子系统,如USB、ALSA、SCSI、NetDev、 Infiniband等。请确保你的驱动程序没有从其他子系统中丢失资源。如果发生这种情况, 典型的症状是当子系统试图调用已经卸载的驱动程序时,会出现Oops(恐慌)。h]hXA大多数低级别的PCI设备驱动程序支持其他一些子系统,如USB、ALSA、SCSI、NetDev、 Infiniband等。请确保你的驱动程序没有从其他子系统中丢失资源。如果发生这种情况, 典型的症状是当子系统试图调用已经卸载的驱动程序时,会出现Oops(恐慌)。}(hjOhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj>hhubeh}(h]id12ah ]h"]从其他子系统取消注册ah$]h&]uh1jhjzhhhhhMubj)}(hhh](j)}(h.禁止设备对MMIO/IO端口地址做出响应h]h.禁止设备对MMIO/IO端口地址做出响应}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjehhhhhMubh)}(hio_unmap() MMIO或IO端口资源,然后调用pci_disable_device()。 这与pci_enable_device()对称相反。 在调用pci_disable_device()后不要访问设备寄存器。h]hio_unmap() MMIO或IO端口资源,然后调用pci_disable_device()。 这与pci_enable_device()对称相反。 在调用pci_disable_device()后不要访问设备寄存器。}(hjvhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjehhubeh}(h]mmio-ioah ]h"].禁止设备对mmio/io端口地址做出响应ah$]h&]uh1jhjzhhhhhMubj)}(hhh](j)}(h释放MMIO/IO端口资源h]h释放MMIO/IO端口资源}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMubh)}(h调用pci_release_region()来标记MMIO或IO端口范围为可用。 如果不这样做,通常会导致无法重新加载驱动程序。h]h调用pci_release_region()来标记MMIO或IO端口范围为可用。 如果不这样做,通常会导致无法重新加载驱动程序。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id13ah ]h"]释放mmio/io端口资源ah$]h&]uh1jhjzhhhhhMubeh}(h]id7ah ]h"]pci设备关闭ah$]h&]uh1jhjhhhhhMHubj)}(hhh](j)}(h如何访问PCI配置空间h]h如何访问PCI配置空间}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMubh)}(hXn你可以使用 `pci_(read|write)_config_(byte|word|dword)` 来访问由 `struct pci_dev *` 表示的设备的配置空间。所有这些函数在成功时返回0,或者返回一个 错误代码( `PCIBIOS_...` ),这个错误代码可以通过pcibios_strerror翻译成文本字 符串。大多数驱动程序希望对有效的PCI设备的访问不会失败。h](h你可以使用 }(hjhhhNhNubhtitle_reference)}(h+`pci_(read|write)_config_(byte|word|dword)`h]h)pci_(read|write)_config_(byte|word|dword)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 来访问由 }(hjhhhNhNubj)}(h`struct pci_dev *`h]hstruct pci_dev *}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhm 表示的设备的配置空间。所有这些函数在成功时返回0,或者返回一个 错误代码( }(hjhhhNhNubj)}(h `PCIBIOS_...`h]h PCIBIOS_...}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh ),这个错误代码可以通过pcibios_strerror翻译成文本字 符串。大多数驱动程序希望对有效的PCI设备的访问不会失败。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h如果你没有可用的pci_dev结构体,你可以调用 `pci_bus_(read|write)_config_(byte|word|dword)` 来访问一个给定的设备和该总 线上的功能。h](h;如果你没有可用的pci_dev结构体,你可以调用 }(hjhhhNhNubj)}(h/`pci_bus_(read|write)_config_(byte|word|dword)`h]h-pci_bus_(read|write)_config_(byte|word|dword)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh; 来访问一个给定的设备和该总 线上的功能。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hs如果你访问配置头的标准部分的字段,请使用中声明的位置和位的符号名称。h]hs如果你访问配置头的标准部分的字段,请使用中声明的位置和位的符号名称。}(hj2hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h如果你需要访问扩展的PCI功能寄存器,只要为特定的功能调用pci_find_capability(), 它就会为你找到相应的寄存器块。h]h如果你需要访问扩展的PCI功能寄存器,只要为特定的功能调用pci_find_capability(), 它就会为你找到相应的寄存器块。}(hj@hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id14ah ]h"]如何访问pci配置空间ah$]h&]uh1jhjhhhhhMubj)}(hhh](j)}(h其它有趣的函数h]h其它有趣的函数}(hjYhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjVhhhhhMubj)}(hhh]j)}(hhh](j)}(hhh]h}(h]h ]h"]h$]h&]colwidthKuh1jhjjubj)}(hhh]h}(h]h ]h"]h$]h&]colwidthK1uh1jhjjubj)}(hhh](j )}(hhh](j)}(hhh]h)}(hpci_get_domain_bus_and_slot()h]hpci_get_domain_bus_and_slot()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h找到与给定的域、总线和槽以及编号相对应的pci_dev。 如果找到该设备,它的引用计数就会增加。h]h找到与给定的域、总线和槽以及编号相对应的pci_dev。 如果找到该设备,它的引用计数就会增加。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_set_power_state()h]hpci_set_power_state()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h+设置PCI电源管理状态(0=D0 ... 3=D3h]h+设置PCI电源管理状态(0=D0 ... 3=D3}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_find_capability()h]hpci_find_capability()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h0在设备的功能列表中找到指定的功能h]h0在设备的功能列表中找到指定的功能}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_resource_start()h]hpci_resource_start()}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj,ubah}(h]h ]h"]h$]h&]uh1jhj)ubj)}(hhh]h)}(h3返回一个给定的PCI区域的总线起始地址h]h3返回一个给定的PCI区域的总线起始地址}(hjFhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjCubah}(h]h ]h"]h$]h&]uh1jhj)ubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_resource_end()h]hpci_resource_end()}(hjfhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjcubah}(h]h ]h"]h$]h&]uh1jhj`ubj)}(hhh]h)}(h*返回给定PCI区域的总线末端地址h]h*返回给定PCI区域的总线末端地址}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjzubah}(h]h ]h"]h$]h&]uh1jhj`ubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_resource_len()h]hpci_resource_len()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h$返回一个PCI区域的字节长度h]h$返回一个PCI区域的字节长度}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_set_drvdata()h]hpci_set_drvdata()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h.为一个pci_dev设置私有驱动数据指针h]h.为一个pci_dev设置私有驱动数据指针}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_get_drvdata()h]hpci_get_drvdata()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h.返回一个pci_dev的私有驱动数据指针h]h.返回一个pci_dev的私有驱动数据指针}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(h pci_set_mwi()h]h pci_set_mwi()}(hjBhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj?ubah}(h]h ]h"]h$]h&]uh1jhj<ubj)}(hhh]h)}(h启用设备内存写无效h]h启用设备内存写无效}(hjYhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjVubah}(h]h ]h"]h$]h&]uh1jhj<ubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_clear_mwi()h]hpci_clear_mwi()}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjvubah}(h]h ]h"]h$]h&]uh1jhjsubj)}(hhh]h)}(h关闭设备内存写无效h]h关闭设备内存写无效}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjsubeh}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhjjubeh}(h]h ]h"]h$]h&]colsKuh1jhjgubah}(h]h ]h"]h$]h&]uh1jhjVhhhhhNubeh}(h]id15ah ]h"]其它有趣的函数ah$]h&]uh1jhjhhhhhMubj)}(hhh](j)}(h 杂项提示h]h 杂项提示}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMubh)}(h当向用户显示PCI设备名称时(例如,当驱动程序想告诉用户它找到了什么卡时),请使 用pci_name(pci_dev)。h]h当向用户显示PCI设备名称时(例如,当驱动程序想告诉用户它找到了什么卡时),请使 用pci_name(pci_dev)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hX始终通过对pci_dev结构体的指针来引用PCI设备。所有的PCI层函数都使用这个标识, 它是唯一合理的标识。除了非常特殊的目的,不要使用总线/插槽/功能号————在有多个 主总线的系统上,它们的语义可能相当复杂。h]hX始终通过对pci_dev结构体的指针来引用PCI设备。所有的PCI层函数都使用这个标识, 它是唯一合理的标识。除了非常特殊的目的,不要使用总线/插槽/功能号————在有多个 主总线的系统上,它们的语义可能相当复杂。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h不要试图在你的驱动程序中开启快速寻址周期写入功能。总线上的所有设备都需要有这样 的功能,所以这需要由平台和通用代码来处理,而不是由单个驱动程序来处理。h]h不要试图在你的驱动程序中开启快速寻址周期写入功能。总线上的所有设备都需要有这样 的功能,所以这需要由平台和通用代码来处理,而不是由单个驱动程序来处理。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id16ah ]h"] 杂项提示ah$]h&]uh1jhjhhhhhMubj)}(hhh](j)}(h供应商和设备标识h]h供应商和设备标识}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMubh)}(h不要在include/linux/pci_ids.h中添加新的设备或供应商ID,除非它们是在多个驱 动程序中共享。如果有需要的话,你可以在你的驱动程序中添加私有定义,或者直接使用 普通的十六进制常量。h]h不要在include/linux/pci_ids.h中添加新的设备或供应商ID,除非它们是在多个驱 动程序中共享。如果有需要的话,你可以在你的驱动程序中添加私有定义,或者直接使用 普通的十六进制常量。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hs设备ID是任意的十六进制数字(厂商控制),通常只在一个地方使用,即pci_device_id 表。h]hs设备ID是任意的十六进制数字(厂商控制),通常只在一个地方使用,即pci_device_id 表。}(hj'hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h请务必提交新的供应商/设备ID到https://pci-ids.ucw.cz/。在 https://github.com/pciutils/pciids,有一个pci.ids文件的镜像。h](hH请务必提交新的供应商/设备ID到https://pci-ids.ucw.cz/。在 }(hj5hhhNhNubj=)}(h"https://github.com/pciutils/pciidsh]h"https://github.com/pciutils/pciids}(hj=hhhNhNubah}(h]h ]h"]h$]h&]refurij?uh1j<hj5ubh%,有一个pci.ids文件的镜像。}(hj5hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id17ah ]h"]供应商和设备标识ah$]h&]uh1jhjhhhhhMubj)}(hhh](j)}(h过时的函数h]h过时的函数}(hjahhhNhNubah}(h]h ]h"]h$]h&]uh1jhj^hhhhhMubh)}(h当你试图将一个旧的驱动程序移植到新的PCI接口时,你可能会遇到几个函数。它们不再存 在于内核中,因为它们与热插拔或PCI域或具有健全的锁不兼容。h]h当你试图将一个旧的驱动程序移植到新的PCI接口时,你可能会遇到几个函数。它们不再存 在于内核中,因为它们与热插拔或PCI域或具有健全的锁不兼容。}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj^hhubj)}(hhh]j)}(hhh](j)}(hhh]h}(h]h ]h"]h$]h&]colwidthKuh1jhjubj)}(hhh]h}(h]h ]h"]h$]h&]colwidthK#uh1jhjubj)}(hhh](j )}(hhh](j)}(hhh]h)}(hpci_find_device()h]hpci_find_device()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h被pci_get_device()取代h]h被pci_get_device()取代}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_find_subsys()h]hpci_find_subsys()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h被pci_get_subsys()取代h]h被pci_get_subsys()取代}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_find_slot()h]hpci_find_slot()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hhh]h)}(h&被pci_get_domain_bus_and_slot()取代h]h&被pci_get_domain_bus_and_slot()取代}(hj%hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj"ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1j hjubj )}(hhh](j)}(hhh]h)}(hpci_get_slot()h]hpci_get_slot()}(hjEhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjBubah}(h]h ]h"]h$]h&]uh1jhj?ubj)}(hhh]h)}(h&被pci_get_domain_bus_and_slot()取代h]h&被pci_get_domain_bus_and_slot()取代}(hj\hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjYubah}(h]h ]h"]h$]h&]uh1jhj?ubeh}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]colsKuh1jhj}ubah}(h]h ]h"]h$]h&]uh1jhj^hhhhhNubh)}(hu另一种方法是传统的PCI设备驱动,即走PCI设备列表。这仍然是可能的,但不鼓励这样做。h]hu另一种方法是传统的PCI设备驱动,即走PCI设备列表。这仍然是可能的,但不鼓励这样做。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj^hhubeh}(h]id18ah ]h"]过时的函数ah$]h&]uh1jhjhhhhhMubj)}(hhh](j)}(hMMIO空间和“写通知”h]hMMIO空间和“写通知”}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhMubh)}(hX将驱动程序从使用I/O端口空间转换为使用MMIO空间,通常需要一些额外的改变。具体来说, 需要处理“写通知”。许多驱动程序(如tg3,acenic,sym53c8xx_2)已经做了这个。I/O 端口空间保证写事务在CPU继续之前到达PCI设备。对MMIO空间的写入允许CPU在事务到达PCI 设备之前继续。HW weenies称这为“写通知”,因为在事务到达目的地之前,写的完成被“通知” 给CPU。h]hX将驱动程序从使用I/O端口空间转换为使用MMIO空间,通常需要一些额外的改变。具体来说, 需要处理“写通知”。许多驱动程序(如tg3,acenic,sym53c8xx_2)已经做了这个。I/O 端口空间保证写事务在CPU继续之前到达PCI设备。对MMIO空间的写入允许CPU在事务到达PCI 设备之前继续。HW weenies称这为“写通知”,因为在事务到达目的地之前,写的完成被“通知” 给CPU。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h因此,对时间敏感的代码应该添加readl(),CPU在做其他工作之前应该等待。经典的“位脉冲” 序列对I/O端口空间很有效::h]h因此,对时间敏感的代码应该添加readl(),CPU在做其他工作之前应该等待。经典的“位脉冲” 序列对I/O端口空间很有效:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(hhfor (i = 8; --i; val >>= 1) { outb(val & 1, ioport_reg); /* 置位 */ udelay(10); }h]hhfor (i = 8; --i; val >>= 1) { outb(val & 1, ioport_reg); /* 置位 */ udelay(10); }}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjhhubh)}(h0对MMIO空间来说,同样的顺序应该是::h]h/对MMIO空间来说,同样的顺序应该是:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(hfor (i = 8; --i; val >>= 1) { writeb(val & 1, mmio_reg); /* 置位 */ readb(safe_mmio_reg); /* 刷新写通知 */ udelay(10); }h]hfor (i = 8; --i; val >>= 1) { writeb(val & 1, mmio_reg); /* 置位 */ readb(safe_mmio_reg); /* 刷新写通知 */ udelay(10); }}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjhhubh)}(hX重要的是, ``safe_mmio_reg`` 不能有任何干扰设备正确操作的副作用。h](h重要的是, }(hjhhhNhNubj)}(h``safe_mmio_reg``h]h safe_mmio_reg}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j~hjubh7 不能有任何干扰设备正确操作的副作用。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hX{另一种需要注意的情况是在重置PCI设备时。使用PCI配置空间读数来刷新writeel()。如果预期 PCI设备不响应readl(),这将在所有平台上优雅地处理PCI主控器的中止。大多数x86平台将允许 MMIO读取主控中止(又称“软失败”),并返回垃圾(例如~0)。但许多RISC平台会崩溃(又称“硬失败”)。h]hX{另一种需要注意的情况是在重置PCI设备时。使用PCI配置空间读数来刷新writeel()。如果预期 PCI设备不响应readl(),这将在所有平台上优雅地处理PCI主控器的中止。大多数x86平台将允许 MMIO读取主控中止(又称“软失败”),并返回垃圾(例如~0)。但许多RISC平台会崩溃(又称“硬失败”)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]mmioah ]h"]mmio空间和“写通知”ah$]h&]uh1jhjhhhhhMubeh}(h]( linux-pcijeh ]h"](如何写linux pci驱动cn_pci_pci.rsteh$]h&]uh1jhhhhhhhKexpect_referenced_by_name}j2jsexpect_referenced_by_id}jjsubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jN generatorN datestampN source_linkN source_urlN toc_backlinksjfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerj[error_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourcehnj _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}j]jasnameids}(j2jj1j.jjj$j!jjj5 j2 jwjtj j j j jt jq j j j j jojljjjjjjjjj;j8jbj_jjjjjSjPjjjjj[jXjjj)j&u nametypes}(j2j1jj$jj5 jwj j jt j j jojjjjj;jbjjjSjjj[jj)uh}(jjj.jjjj!jjjj2 j'jtj8 j j1 j j jq j j jw j j jlj jjzjjNjjjjj8jj_j>jjejjjPjjjVjjjXjjj^j&ju footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jiKsRparse_messages]transform_messages]hsystem_message)}(hhh]h)}(hhh]h4Hyperlink target "cn-pci-pci-rst" is not referenced.}hjsbah}(h]h ]h"]h$]h&]uh1hhjubah}(h]h ]h"]h$]h&]levelKtypeINFOsourcehnjlineKuh1juba transformerN include_log],Documentation/translations/zh_CN/PCI/pci.rst(NNNNta decorationNhhub.