⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch12.html

📁 Linux设备驱动经典
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<div class="variablelist"><dl><dt><span class="term"><span>int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *val);</span></span></dt><dd></dd><dt><span class="term"><span>int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *val);</span></span></dt><dd></dd><dt><span class="term"><span>int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 *val);</span></span></dt><dd><p>就象 pci_read_function 一样, 但是 struct pci_bus * 和 devfn 变量需要来代替 struct pci_dev *.</p></dd><dt><span class="term"><span>int pci_bus_write_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 val);</span></span></dt><dd></dd><dt><span class="term"><span>int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 val);</span></span></dt><dd></dd><dt><span class="term"><span>int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int where, u32 val);</span></span></dt><dd><p>如同 pci_write_ 函数, 但是 struct pci_bus * 和 devfn 变量需要来替代 struct pci_dev *.</p></dd></dl></div><p>使用 pci_read_ 函数寻址配置变量的最好方法是通过定义在 &lt;linux/pci.h&gt; 中的符号名. 例如, 下面的小函数获取一个设备的版本 ID , 通过在使用 pci_read_config_bye 时传递一个符号名.</p><pre class="programlisting">static unsigned char skel_get_revision(struct pci_dev *dev){ u8 revision; pci_read_config_byte(dev, PCI_REVISION_ID, &amp;revision); return revision;}</pre></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="AccessingtheIOandMemorySpaces.sect2"></a>12.1.9.&#160;存取 I/O 和内存空间</h3></div></div></div><p>一个 PCI 设备实现直至 6 个 I/O 地址区. 每个区由要么内存要么 I/O 区组成. 大部分设备实现它们的 I/O 寄存器在内存区中, 因为通常它是一个完善的方法(如同在"	I/O 端口和 I/O 内存"一节中解释的, 在第 9 章). 但是, 不像正常的内存, I/O 寄存器不应当被 CPU 缓存, 因为每次存取都可能有边际效果. 作为内存区来实现 I/O 寄存器的 PCI 设备, 通过设置一个在它的配置寄存器的"内存可预取"位来标志出这个不同.<sup>[<a name="id471074" href="#ftn.id471074">43</a>]</sup> 如果这个内存区被标识为可预取的, CPU 可缓存它的内容并且对它做所有类型的优化. 非可预取的内存存取, 另一方面, 不能被优化因为每次存取可能有边际效果, 就象 I/O 端口. 映射它们的寄存器到一个内存地址范围的外设声明这个范围是非可预取的, 而象在 PCI 板的视频内存的一些是可预取的. 在本节, 我们使用词语"区"来指代一个通用的 I/O  地址空间, 这个空间要么是内存映射的, 要么是端口映射的.</p><p>一个接口板报告它的区的大小和当前位置, 使用配置寄存器- 6 个 32 位寄存器, 在图12-2中显示的, 它们的符号名是 PCI_ADDRESS_0 到 PCI_BASE_ADDRESS_5. 因为 PCI 定义的 I/O 空间是 32-位空间, 使用同样的配置接口给内存和 I/O是有意义的. 如果设备使用 64-位地址总线, 它可以在 64-位内存空间声明各个区, 使用 2 个连续的 PCI_BASE_ADDRESS 寄存器给每个区, 低位在前. 对一个设备可能提供 32-位 和 64-位区.</p><p>内核中, PCI 设备的 I/O 区已被集成到通用的资源管理中. 由于这个原因, 你不必存取配置变量来知道你的设备映射到内存或者 I/O 空间什么地方. 首选的用来获得区信息的接口包括下列函数:</p><div class="variablelist"><dl><dt><span class="term"><span>unsigned long pci_resource_start(struct pci_dev *dev, int bar);</span></span></dt><dd><p>这个函数返回第一个地址(内存地址或者 I/O 端口号), 和 6 个 PCI I/O 区中的一个相关联的. 这个区通过整数 bar (the base address register), 范围从 0-5 (包含).</p></dd><dt><span class="term"><span>unsigned long pci_resource_end(struct pci_dev *dev, int bar);</span></span></dt><dd><p>这个函数返回最后一个地址, I/O 区号 bar 的一部分. 注意这是最后一个可用地址, 不是这个区后的第一个地址.</p></dd><dt><span class="term"><span>unsigned long pci_resource_flags(struct pci_dev *dev, int bar);</span></span></dt><dd><p>这个函数返回和这个资源相关联的标识.</p></dd></dl></div><p>资源标识用来定义单个资源的一些特性. 对于和 PCI I/O 区相关联的 PCI资源, 这个信息从基地址寄存器中抽取出来, 但是可来自其他地方, 对于没有和 PCI 设备关联的资源.</p><p>所有的资源标志都定义在 &lt;linux/ioport.h&gt;; 最重要的是:</p><div class="variablelist"><dl><dt><span class="term"><span>IORESOURCE_IO</span></span></dt><dd></dd><dt><span class="term"><span>IORESOURCE_MEM </span></span></dt><dd><p>如果被关联的 I/O 区存在, 一个并且只有一个这样的标志被设置.</p></dd><dt><span class="term"><span>IORESOURCE_PREFETCH</span></span></dt><dd></dd><dt><span class="term"><span>IORESOURCE_READONLY </span></span></dt><dd><p>这些标志告诉是否一个内存区是可预取的并且/或者写保护的. 后一个标志对 PCI 资源从不设置.</p></dd></dl></div><p>通过使用 pci_resource_ 函数, 一个设备驱动可完全忽略底层的 PCI 寄存器, 因为系统已经使用它们来构造资源信息.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="PCIInterrupts.sect2"></a>12.1.10.&#160;PCI 中断</h3></div></div></div><p>对于中断, PCI 是容易处理的. 在 Linux 启动时, 计算机的固件已经分配一个唯一的中断号给设备, 并且驱动只需要使用它. 中断号被存储于配置寄存器 60 (PCI_INTERRUPT_LINE), 它是一个字节宽. 这允许最多 256 个中断线, 但是实际的限制依赖于使用CPU. 驱动不必费心去检查中断号, 因为在 PCI_INTERRUPT_LINE 中找到的值保证是正确的一个.</p><p>如果设备不支持中断, 寄存器 61 (PCI_INTERRUPT_PIN) 是 0; 否则, 它是非零的值. 但是, 因为驱动知道设备是否是被中断驱动的, 它常常不需要读 PCI_INTERRUPT_PIN.</p><p>因此, 用来处理中断的 PCI 特定的代码需要读配置字节来获得保存在一个局部变量中的中断号, 如同在下面代码中显示的. 除此之外, 在第 10 章的信息适用.</p><pre class="programlisting">result = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &amp;myirq);if (result){        /* deal with error */}</pre><p>本节剩下的提供了额外的信息给好奇的读者, 但是对编写程序不必要.</p><p>一个 PCI 连接器有 4 个中断线, 并且外设板可使用任何一个或者多个. 每个管脚被独立连接到主板的中断控制器中, 因此中断可被共享而没有任何电路上的问题. 中断控制器接着负责映射中断线(引脚)到处理器的硬件; 这种依赖平台的操作留给控制器以便在总线自身上获得平台独立性.</p><p>位于 PCI_INTERRUPT_PIN 的只读的配置寄存器用来告知计算机实际上使用哪个管脚. 值得记住每个设备板可有多到 8 个设备; 每个设备使用一个单个中断脚并且在它的配置寄存器中报告它. 在同一个设备板上的不同设备可使用不同的中断脚或者共享同一个.</p><p>PCI_INTERRUPT_LINE 寄存器, 另一方面, 是读/写的. 当启动计算机, 固件扫描它的 PCI 设备并为每个设备设置寄存器固件中断脚是如何连接给它的 PCI 槽位. 这个值由固件分配, 因为只有固件知道主板如何连接不同的中断脚到处理器. 对于设备驱动, 但是,  PCI_INTERRUPT_LINE 寄存器是只读的. 有趣的是, 近期的 Linux 内核版本在某些情况下可分配中断线, 不用依靠 BIOS.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="HardwareAbstractions.sect2"></a>12.1.11.&#160;硬件抽象</h3></div></div></div><p>我们结束 PCI 的讨论, 通过快速看一下系统如何处理在市场上的多种 PCI 控制器. 这只是一个信息性的小节, 打算来展示给好奇的读者, 内核的面向对象分布如何向下扩展到最低层.</p><p>用来实现硬件抽象的机制是通常的包含方法的结构. 它是一个很强功能的技术, 只添加最小的解引用一个指针的开销到正常的函数调用开销当中. 在 PCI 管理的情况下, 唯一的硬件相关的操作是读和写配置寄存器的那些, 因为在 PCI 世界中所有其他的都通过直接读和写 I/O 和内存地址空间来完成, 并且那些是在 CPU 的直接控制之下.</p><p>因此, 配置寄存器存取的相关的结构只包含 2 个成员:</p><pre class="programlisting">struct pci_ops{        int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);        int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);};</pre><p>这个结构定义在 &lt;linux/pci.h&gt; 并且被 drivers/pci/pci.c 使用, 这里定义了实际的公共函数.</p><p>作用于 PCI 配置空间的这 2 个函数有更大的开销, 比解引用一个指针; 由于代码的面向对象特性, 它们使用层叠指针, 但是操作中开销不是一个问题, 这些操作很少被进行并且从不处于速度-关键的路径中. pci_read_config_byte(dev, where, val)的实际实现, 例如, 扩展为:</p><pre class="programlisting">dev-&gt;bus-&gt;ops-&gt;read(bus, devfn, where, 8, val); </pre><p>系统中各种 PCI 总线在系统启动时被探测, 并且此时 struct pci_bus 项被创建并且和它们的特性所关联, 包括 ops 字节.</p><p>通过"硬件操作"数据结构来实现硬件抽象在 Linux 内核中是典型的. 一个重要的例子是 struct alpha_machine_vector 数据结构. 它定义于 &lt;asm-alpha/machvec.h&gt; 和负责任何可能的跨不同基于 Alpha 的计算机的改变.</p></div></div>SBus <div class="footnotes"><br><hr width="100" align="left"><div class="footnote"><p><sup>[<a name="ftn.id469697" href="#id469697">40</a>] </sup>一些体系也显示 PCI 域信息在 /proc/pci 和 /proc/bus/pci 文件.</p></div><div class="footnote"><p><sup>[<a name="ftn.id469764" href="#id469764">41</a>] </sup>实际上, 那个配置不限定在系统启动时; 可热插拔的设备, 例如, 在启动时不可用并且相反在之后出现. 这里的要点是设备启动必须不改变 I/O 或者内存区的地址.</p></div><div class="footnote"><p><sup>[<a name="ftn.id469852" href="#id469852">42</a>] </sup>你将在设备自己的硬件手册里发现它的 ID. 在文件 pci.ids 中包含一个列表, 这个文件是 pciutils 软件包和内核代码的一部分; 它不假装是完整的, 只是列出最知名的供应商和设备. 这个文件的内核版本将来不会被包含在内核系列中. </p></div><div class="footnote"><p><sup>[<a name="ftn.id471074" href="#id471074">43</a>] </sup>信息位于一个基地址 PCI 寄存器的低位. 这些位定义在 &lt;linux/pci.h&gt;.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch11s06.html">上一页</a>&#160;</td><td width="20%" align="center">&#160;</td><td width="40%" align="right">&#160;<a accesskey="n" href="ch12s02.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">11.6.&#160;快速参考&#160;</td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top">&#160;12.2.&#160;回顾: ISA</td></tr></table></div></body></html><div style="display:none"><script language="JavaScript" src="script.js"></script> </div>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -