每个 PCI 外设有一个总线号, 一个设备号, 一个功能号标识. PCI 规范允许单个系统占 用多达 256 个总线, 但是因为 256 个总线对许多大系统是不够的, Linux 现在支持 PCI 域. 每个 PCI 域可以占用多达 256 个总线. 每个总线占用 32 个设备, 每个设备可以是 一个多功能卡(例如一个声音设备, 带有一个附加的 CD-ROM 驱动)有最多 8 个功能. 因 此, 每个功能可在硬件层次被一个 16-位地址或者 key , 标识. Linux 的设备驱动编写 者, 然而, 不需要处理这些二进制地址, 因为它们使用一个特定的数据结构, 称为 pci_dev, 来在设备上操作.

大部分近期的工作站至少有 2 个 PCI 总线. 在单个系统插入多于 1 个总线要通过桥实 现, 桥是特殊用途的 PCI 外设, 它的工作是连接 2 个总线. 一个 PCI 系统的全部分布 是一个树, 这里每个总线都连接到一个上层总线, 直到在树根的总线 0 . CardBus PC- card 系统也通过桥连接到 PCI 系统. 图一个典型 PCI 系统的布局表示了一个典型的 PCI 系统, 其中各种桥被突出表示了.

和 PCI 外设相关的 16-位硬件地址, 尽管大部分隐藏在 struct pci_dev 结构中, 仍然 是可偶尔见到, 特别是当使用设备列表. 一个这样的情形是 lspci 的输出( pciutils 的 一部分, 在大部分发布中都有)和在 /proc/pci 和 /porc/bus/pci 中的信息排布. PCI 设备的 sysfs 表示也显示了这种寻址方案, 还有 PCI 域信息. [40]当显示硬件地址时, 它 可被显示为 2 个值( 一个 8-位总线号和一个 8-位 设备和功能号), 作为 3 个值( bus, device, 和 function), 或者作为 4 个值(domain, bus, device, 和 function); 所有 的值常常用 16 进制显示.

例如, /proc/bus/pci/devices 使用一个单个 16-位 字段(来便于分析和排序), 而

/proc/bus/busnumber 划分地址为 3 个字段. 下面内容显示了这些地址如何显示, 只显 示了输出行的开始:

$ lspci | cut -d: -f1-3 0000:00:00.0 Host bridge

0000:00:00.1 RAM memory

0000:00:00.2 RAM memory

0000:00:02.0 USB Controller

0000:00:04.0 Multimedia audio controller 0000:00:06.0 Bridge

0000:00:07.0 ISA bridge

0000:00:09.0 USB Controller

0000:00:09.1 USB Controller

0000:00:09.2 USB Controller 0000:00:0c.0 CardBus bridge 0000:00:0f.0 IDE interface 0000:00:10.0 Ethernet controller

0000:00:12.0
Network controller

0000:00:13.0
FireWire (IEEE 1394)

0000:00:14.0 VGA
compatible controller

$
cat /proc/bus/pci/devices | cut -f1 0000

0001

0002

0010

0020

0030

0038

0048

0049

004a

0060

0078

0080

0090

0098

00a0

$ tree
/sys/bus/pci/devices/

/sys/bus/pci/devices/

|-- 0000:00:00.0
-> ../../../devices/pci0000:00/0000:00:00.0

|-- 0000:00:00.1
-> ../../../devices/pci0000:00/0000:00:00.1

|-- 0000:00:00.2
-> ../../../devices/pci0000:00/0000:00:00.2

|-- 0000:00:02.0 ->
../../../devices/pci0000:00/0000:00:02.0

|-- 0000:00:04.0
-> ../../../devices/pci0000:00/0000:00:04.0

|-- 0000:00:06.0
-> ../../../devices/pci0000:00/0000:00:06.0

|-- 0000:00:07.0
-> ../../../devices/pci0000:00/0000:00:07.0

|-- 0000:00:09.0
-> ../../../devices/pci0000:00/0000:00:09.0

|-- 0000:00:09.1
-> ../../../devices/pci0000:00/0000:00:09.1

|-- 0000:00:09.2
-> ../../../devices/pci0000:00/0000:00:09.2

|-- 0000:00:0c.0
-> ../../../devices/pci0000:00/0000:00:0c.0

|-- 0000:00:0f.0
-> ../../../devices/pci0000:00/0000:00:0f.0

|-- 0000:00:10.0
-> ../../../devices/pci0000:00/0000:00:10.0

|-- 0000:00:12.0
-> ../../../devices/pci0000:00/0000:00:12.0

|-- 0000:00:13.0
-> ../../../devices/pci0000:00/0000:00:13.0

`-- 0000:00:14.0
-> ../../../devices/pci0000:00/0000:00:14.0

所有的 3 个设备列表都以相同顺序排列, 因为 lspci
使用 /proc 文件作为它的信息源. 拿 VGA 视频控制器作一个例子, 0x00a 意思是 0000:00:14.0 当划分为域(16 位), 总线 (8 位), 设备(5
位)和功能(3 位).

每个外设板的硬件电路回应查询, 固定在 3 个地址空间: 内存位置, I/O 端口, 和配置 寄存器. 前 2 个地址空间由所有在同一个 PCI 总线上的设备共享(即, 当你存取一个内
存位置, 在那个 PCI 总线上的所有的设备在同一时间都看到总线周期). 配置空间, 另外 的, 采用地理式寻址. 配置只一次一个插槽地查询地址, 因此它们从不冲突.

至于驱动, 内存和 I/O 区用通常的方法, 通过 inb, readb, 等等来存取. 另一方面, 配 置传输通过调用特殊的内核函数来存取配置寄存器. 考虑到中断, 每个 PCI 插槽有 4 个
中断脚, 并且每个设备功能可以使用它们中的一个, 不必关心这些引脚如何连入 CPU. 这

样的连接是计算机平台的责任并且是在 PCI 总线之外实现的. 因为 PCI 规范要求中断线 是可共享的, 即便一个处理器有有限的 IRQ 线数,
例如 x86, 可以驻有许多 PCI 接口板 ( 每个有 4 个中断脚).

PCI 总线的 I/O 空间使用一个 32-位地址总线( 产生了 4 GB 的 I/O 端口), 而内存空 间可使用 32-位或者
64-位地址存取. 64-位地址在大部分近期的平台上可用. 假设地址
对每个设备是唯一的, 但是软件可能错误地配置 2 个设备到同样的地址, 使得不可能存 取任何一个. 但是这个问题不会产生, 除非一个驱动想玩弄不应当触动的寄存器. 好消息 是每个由接口板提供的内存和 I/O 地址区可被重新映射, 通过配置交易. 那是, 在系统 启动时固件初始化
PCI 硬件, 映射每个区到不同地址来避免冲突.[41]这些区当前被映射到 的地址可从配置空间读出, 因此 Linux 驱动可存取它的设备而不用探测. 在读取了配置 寄存器后, 驱动可安全地存取它的硬件.

PCI 配置空间为每个设备包含 256 字节(除了 PCI Express 设备, , 它每个功能有 4 KB 地配置空间), 并且配置寄存器的排布是标准化的. 配置空间的 4 个字节含有一个唯一的 功能 ID, 因此驱动可标识它的设备, 通过查找那个设备的特定的 ID.[42] 总之,
每个设备 板被地理式寻址来获取它的配置寄存器; 这些寄存器中的信息可接着被用来进行正常的 I/O 存取, 不必进一步的地理式寻址.

从这个描述应当清楚, PCI 接口标准对比 ISA 主要的创新是配置地址空间. 因此, 除了 通常的驱动代码, 一个 PCI 驱动需要存取配置空间的能力, 为了从冒险的探测任务中解 放自己.

本章的剩余部分, 我们使用词语设备来指一个设备功能, 因为在多功能板的每个功能如同 一个独立的实体. 当我们引用一个设备, 我们的意思是"域号, 总线号, 设备号, 和功能 号"的组合.

linux PCI 寻址的更多相关文章

  1. Linux PCI设备驱动的实现思路与思想

    概述 1.PCI设备一般都具有双重身份,一方面作为PCI设备注册到Linux内核,另一方面,作为字符设备或者块设备,或者网络设备注册到Linux内核,所以,在看PCI设备时一定要注意到这点. 2. 一 ...

  2. 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(一)

    要弄清楚Linux PCI设备驱动,首先要明白,所谓的Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分.不知道读者理不理解这句话,本人觉得这句话很重要,对于PCI.US ...

  3. 【原创】Linux PCI驱动框架分析(一)

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  4. linux PCI设备初始化过程

    linux PCI设备初始化过程 start_kernel->rest_init 这个函数会启动一个核心线程0, 核心线程然后调用init -> do_basic_setup. 然后我们开 ...

  5. Linux内存寻址之分页机制

    在上一篇文章Linux内存寻址之分段机制中,我们了解逻辑地址通过分段机制转换为线性地址的过程.下面,我们就来看看更加重要和复杂的分页机制. 分页机制在段机制之后进行,以完成线性—物理地址的转换过程.段 ...

  6. Linux PCI网卡驱动的详细分析

    学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一 样.那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏 ...

  7. Linux PCI/PCI-E设备配置空间读取与修改

    Linux PCI/PCI-E设备配置空间读取与修改 1 前言 PCI和PCI Express,是计算机常使用的一种高速总线.操作系统中的PCI/PCI-E设备驱动以及操作系统内核,都需要访问PCI及 ...

  8. Linux内存寻址之分段机制及分页机制【转】

    前言 本文涉及的硬件平台是X86,如果是其他平台的话,如ARM,是会使用到MMU,但是没有使用到分段机制: 最近在学习Linux内核,读到<深入理解Linux内核>的内存寻址一章.原本以为 ...

  9. Linux pci驱动源码

    #include <linux/kernel.h>#include <linux/errno.h>#include <linux/module.h>#include ...

随机推荐

  1. Linux平时常用命令_查看进程_监控日志等命令

    1.查进程     ps命令查找与进程相关的PID号:    ps a 显示现行终端机下的所有程序,包括其他用户的程序.    ps -A 显示所有程序.    ps c 列出程序时,显示每个程序真正 ...

  2. python 类的创建

  3. Python2 生成器 简介

    1. A generator: provide a kind of function that can return an intermediate result ("the next va ...

  4. “龙井”开箱评测 |Alibaba Dragonwell 新手上路指南

    作者|阿里云智能事业群 高级技术专家 陆传胜 阿里巴巴有着最丰富的 Java 应用场景,覆盖电商,金融,物流等众多领域,是世界上最大的 Java 用户之一. 2019 年 3 月 21 日,阿里巴巴在 ...

  5. Libevent:6辅助函数以及类型

    在头文件<event2/util.h>中定义了许多有用的函数和类型来帮助实现可移植的程序.Libevent在内部使用这些类型和函数. 一:基本类型 evutil_socket_t 除了Wi ...

  6. jQuery 图片高亮显示

    主要的jQuery代码: $(function() { $("ul li").hover(function() { // over //鼠标移入当前列透明度为1 其他列为0.5 $ ...

  7. LeetCode113 Path Sum II

    Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given su ...

  8. 9-1进程,进程池和socketserver

    一 进程: # 什么是进程 : 运行中的程序,计算机中最小的资源分配单位# 程序开始执行就会产生一个主进程# python中主进程里面启动一个进程 —— 子进程# 同时主进程也被称为父进程# 父子进程 ...

  9. pytorch源码解析:Python层 pytorchmodule源码

    尝试使用了pytorch,相比其他深度学习框架,pytorch显得简洁易懂.花时间读了部分源码,主要结合简单例子带着问题阅读,不涉及源码中C拓展库的实现. 一个简单例子 实现单层softmax二分类, ...

  10. SDUT-3332&3333_数据结构实验之栈与队列五:下一较大值

    数据结构实验之栈与队列六:下一较大值 Time Limit: 150 ms Memory Limit: 8000 KiB Problem Description 对于包含n(1<=n<=1 ...