PCI总线及LSPCI命令详解分析

lspci命令详解参考  https://blog.csdn.net/huangxuesi/article/details/69959679

一、PCI简介
     PCI是一种外设总线规范。我们先来看一下什么是总线:总线是一种传输信号的路径或信道。典型情况是,总线是连接于一个或多个导体的电气连线,总 线上连接的所有设备可在同一时间收到所有的传输内容。总线由电气接口和编程接口组成。本文讨论Linux 下的设备驱动,所以,重点关注编程接口。
     PCI是Peripheral Component Interconnect(外围设备互联)的简称,是普遍使用在桌面及更大型的计算机上的外 设总线。PCI架构被设计为ISA标准的替代品,它有三个主要目标:获得在计算机和外设之间传输数据时更好的性能;尽可能的平台无关;简化往系统中添加和 删除外设的工作。

二、PCI寻址
     从现在开始,我想尽可能通过一些实际的例子来说明问题,而减少理论方面的问题的描述,因为,相关的理论的东西,可以在其它地方找到。
     我们先来看一个例子,我的电脑装有1G的RAM,1G以后的物理内存地址空间都是外部设备IO在系统内存地址空间上的映射。 /proc/iomem描述了系统中所有的设备I/O在内存地址空间上的映射。我们来看地址从1G开始的第一个设备在/proc/iomem中是如何描述 的:
             40000000-400003ff : 0000:00:1f.1
     这是一个PCI设备,40000000-400003ff是它所映射的内存地址空间,占据了内存地址空间的1024 bytes的位置,而 0000:00:1f.1则是一个PCI外设的地址,它以冒号和逗号分隔为4个部分,第一个16位表示域,第二个8位表示一个总线编号,第三个5位表示一 个设备号,最后是3位,表示功能号。

     因为PCI规范允许单个系统拥有高达256个总线,所以总线编号是8位。但对于大型系统而言,这是不够的,所以,引入了域的概念,每个 PCI域可以拥有最多256个总线,每个总线上可支持32个设备,所以设备号是5位,而每个设备上最多可有8种功能,所以功能号是3位。由此,我们可以得 出上述的PCI设备的地址是0号域0号总线上的31号设备上的1号功能。

     那上述的这个PCI设备到底是什么呢?下面是我的电脑上的lspci命令的输出:
     00:00.0 Host bridge: Intel Corporation 82845 845 (Brookdale) Chipset Host Bridge (rev 04)
     00:01.0 PCI bridge: Intel Corporation 82845 845 (Brookdale) Chipset AGP Bridge(rev 04)
     00:1d.0 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #1) (rev 02)
     00:1d.1 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #2) (rev 02)
     00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 42)
     00:1f.0 ISA bridge: Intel Corporation 82801CAM ISA Bridge (LPC) (rev 02)
     00:1f.1 IDE interface: Intel Corporation 82801CAM IDE U100 (rev 02)
     00:1f.3 SMBus: Intel Corporation 82801CA/CAM SMBus Controller (rev 02)
     00:1f.5 Multimedia audio controller:Intel Corporation 82801CA/CAM AC'97 Audio Controller (rev 02)
     00:1f.6 Modem: Intel Corporation 82801CA/CAM AC'97 Modem Controller (rev 02)
     01:00.0 VGA compatible controller: nVidia Corporation NV17 [GeForce4 420 Go](rev a3)
     02:00.0 FireWire (IEEE 1394): VIA Technologies, Inc. IEEE 1394 Host Controller(rev 46)
     02:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+(rev 10)
     02:04.0 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)
     02:04.1 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)
     lspci没有标明域,但对于一台PC而言,一般只有一个域,即0号域。通过这个输出我们可以看到它是一个IDE interface。由上述的 输出可以看到,我的电脑上共有3个PCI总线(0号,1号,2号)。在单个系统上,插入多个总线是通过桥(bridge)来完成的,桥是一种用来连接总线 的特殊PCI外设。所以,PCI系统的整体布局组织为树型,我们可以通过上面的lspci输出,来画出我的电脑上的PCI系统的树型结构:
00:00.0(主桥)--00:01.0(PCI桥)-----01:00:0(nVidia显卡)
                   |
                   |---00:1d(USB控制器)--00:1d:0(USB1号控制器)
                   |                    |
                   |                    |--00:1d:1(USB2号控制器)                    |
                   |-00:1e:0(PCI桥)--02:00.0(IEEE1394)
                   |                |
                   |                |-02:01.0(8139网卡)
                   |                |
                   |                |-02:04(CardBus桥)-02:04.0(桥1)
                   |                                   |
                   |                                   |--02:04.1(桥2)
                   |
                   |-00:1f(多功能板卡)-00:1f:0(ISA桥)
                                        |
                                        |--00:1f:1(IDE接口)
                                        |
                                        |--00:1f:3(SMBus)
                                        |
                                        |--00:1f:5(多媒体声音控制器)
                                        |
                                        |--00:1f:6(调制解调器)
     由上图可以得出,我的电脑上共有8个PCI设备,其中0号总线上(主桥)上连有4个,1号总线上连有1个,2号总线上连有3个。00:1f是一个连有5个功能的多功能板卡。
     每一个PCI设备都有它映射的内存地址空间和它的I/O区域,这点是比较容易理解的。除此之外,PCI设备还有它的配置寄存器。有了配置寄存器, PCI的驱动程序就不需要探测就能访问设备。配置寄存器的布局是标准化的,配置空间的4个字节含有一个独一无二的功能ID,因此,驱动程序可通过查询外设 的特定 ID来识别其设备。所以,PCI接口标准在ISA之上的主要创新在于配置地址空间。
前文已讲过,PCI驱动程序不需要探测就能访问设备,而这得益于配置地址空间。在系统引导阶段,PCI硬件设备保持未激活状态,但每个PCI主板均配备有能够处理PCI的固件,固件通过读写PCI控制器中的寄存器,提供了对设备配置地址空间的访问。
     配置地址空间的前64字节是标准化的,它提供了厂商号,设备号,版本号等信息,唯一标识一个PCI设备。同时,它也提供了最多可多达6个的I/O 地址区域,每个区域可以是内存也可以是I/O地址。这几个I/O地址区域是驱动程序找到设备映射到内存和I/O空间的具体位置的唯一途径。有了这两点, PCI驱动程序就完成了相当于探测的功能。关于这64个字节的配置空间的详细情况,可参阅《Linux设备驱动程序第三版》P306,不再详述。

PCI设备拥有256B的空间,用于存放配置寄存器。改空间是辨别PCI卡型号和性能的关键。我们可以查看配置去的详细情况

PCI寄存器是小端字节序格式,以通过sysfs来查看PCI的配置,如下命令,查看命令和结果如下

对上图进行解释,前面两字节是厂家ID,表示生产厂家。PCI厂家ID在全球都是统一分配和管理的,在www.pcidatabase.com可以查看。PCI配置空间含义如下

对应上上图的输出信息,翻译起来就是

厂家ID是0x1002,设备ID是6778,设备类型代码是0300,基址寄存器是0x000c-0000

子厂家ID是174b,子设备ID是d145,

配置空间中包含10字节用于表示设备类型的编码。表示PCI桥设备类编码的第一个字节是0x06,网络设备类编码的第一个字节是0x02,类编码类型的定义见include/linux/pci_ids.h

PCI驱动程序向PCI子系统注册其支持的厂家ID、设备ID和设备类编码。使用插入的卡通过配置空间被识别后,PCI子系统把插入的卡和对应的驱动绑定
 

访问PCI

PCI设备包括3个寻址空间:配置空间、IO端口和设备内存。

配置区

内核为驱动程序提供6个可以调用的函数来访问PCI配置区:

pci_read_config_[byte|word|dword](struct pci_dev *pdev,int offset,int *value);

pci_write_config_[byte|word|dword](struct pci_dev *pdev,int offset,int *value);

在参数列表中,strutc pci_dev是PCI设备结构体,offset是想访问的配置空间中的字节位置。对读函数来讲,value是数据缓冲区的指针;对写函数来讲,value放的是准备写的数据。

看看下面的例子。

要得到分配给某卡功能的中断号,进行如下操作

unsigend char_irq;

pci_read_config_byte(pdev,PCI_INTERRUPT_LINE,&irq);

按照PCI规定说明,PCI配置空间偏移量地址60处存放卡的IRQ号,配置空间的偏移地址都在文件include/linux/pci_regs.h中有详细定义,所以一般用到PCI_INTERRUPT_LINE 表示偏移地址。于此类似,要想读PCI状态寄存器(配置空间偏移6处),进行如下操作:

unsigned short status;  pci_read_config_word(pdev,PCI_STATUS,&status);

配置去开头的64B是规范了的。其他地方由设备生产厂家按照自己的意思定义。

比如说我们使用的Xircom卡,在64B偏移处的4个字节用于电源管理的目的,为了禁止电灌管理功能,Xircom CardBus驱动程序在实现文件drivers/net/tulip/xircom_cb.c里进行如下操作:

#define PCI_POWERMGMT 0x40

pci_write_config_dword(pdev,PCI_POWERMGMT,0x0000);

 

IO和内存

PCI卡有6个IO或内存区域,I/O区域包括寄存器,内存区域存放数据。例如:视频卡的I/0区域包含控制寄存器,内存区域映射缓冲。但并不是所有的卡都有可寻址的内存区域。

I/O和内存区域的功能和硬件有关系,在设备手册中可以查到。

 

像配置区一样,内核提供一系列的辅助函数,可以用来操作PCI设备的/O和内存区域

Unsigned long pci_resource_[start|len|end|flag](struct pci_dev *pdev,int bar);为操作I/O区域。如PCI视频卡设备控制寄存器,驱动程序要完成以下事件

(1)从配置区相应基址寄存器里得到I/O区域的基址:

unsigned long io_base = pci_resource_start(pdev,bar);

这里假定设备控制寄存器被映射到由变量bar关联的I/O区域,bar值的变化范围是0-5。

(2)用内核的request_region()常规机制获得这个I/O区域,并标明它对应的设备:

request_region(io_base,length,”my_driver”);

通过调用request_region()申请I/O地址后,设备驱动程序在申请的I/O地址中操作运行。此机制确保其他程序不能申请此区域,直至用过调用release_region()释放占用的区域。

 

lenth用于控制寄存器空间的大小,my_driver表示这个区域的使用者。在文件/proc/ioports中的my_driver对应的文本行里可以看到这片I/O区域。也可以使用drivers/pci/pci.c文件中定义的封装函数pci_request_region()申请I/O端口

(3)用寄存器手册上的偏移地址加上(1)得到的基址,然后用inb()和outb()函数来访问这些寄存器

Read函数

Register_data = inl(io_base+REGISTER_OFFSET);

write函数

Outl(register_data,io_base+REGISTER_OFFSET)

为了能够操作PCI设备的内存区域,按照下面步骤进行

(1)获得基址、内存区域长度以及内存相关标志

Unsigned long mmio_base=pci_resource_start(pdev,bar)

Unsigned long mmio_length=pci_resource_length(pdev,bar)

Unsigned long mmio_flags=pci_resource_flags(pdev,bar)

这里假定设备内存区域被映射到基址寄存器bar

(2)用内核的request_men_region()常规机制标记这片内存区的拥有者

Request_men_region(mmio_base,mmio_length,”my_driver”);

用前面提到的封装函数pci_request_region()也可以

(3)让CPU访问第(1)步获得的设备内存。为防止发生意外,某些内存空间(如包含寄存器的地方)设置成不让CPU可预取或不可启用高速缓存。对于其他设备(如本例中提到的区域),CPU 可以启用高速缓存。根据内存区域的访问标志,使用合适的函数可以得到与映射区域相对应的内核虚拟地址

Viod __iomen *buffer;

If(flages & IORESOURCE_CACHEABLE)

{

Buffer=ioremap(mmio_base,mmio_length);

}

Else

{

Buffer = ioremap_nocache(mmio_base,mmio_length);

}

为了安全起见并避免执行前述的检查,使用lib/iomap.c定义的函数pci_iomap()代替

  Buffer=pci_iomap(pdev,bar,mmio_length)

 

 

DMA缓冲区是DMA传送时用做源端地址或者目的地址的内存区。如果总线接口能访问的地址受限,将会影响DMA缓冲区的大小。因此,用于24位总线(如ISA)的DMA缓冲区只占系统内存底部的16MB。称为ZONE_DMA。PCI总线默认为32位,所以在32的平台下一般不会碰到这种限制。可以用下面的函数告诉内核系统中可用作DMA缓冲区的地址的特殊要求:

dma_set_mask(struct device *dev,u64 mask);

如果这个函数返回成功,就可以在mask指定的地址范围内进行DMA操作。如e1000PCI-X吉位以太网驱动程序(drivers/net/e1000/e1000-main.c)里:

If(!(err = pci_set_dma_mask(pdev,DMA_64)))

{

/*system supports 64-bit DMA*/

Pci_using_dac =1

}

Else

{

/*see if 32-bit DMA is supported */

If((err =pci_set_dma_mask(pdev,DMA_32BIT_MASK)))

{

/*no,let’s abort*/

E1000_ERR(“No usable DMA configuration,aborting\n”);

Return err;

}

/*32-bit DMA*/

Pci_using_dac=0;

}

I/O设备从总线控制器或IOMMU(I/O Memory Unit,I/O内存管理单元)的角度看待DMA缓冲区。所以,I/O设备需要的是DMA缓冲区的总线地址,而不是物理地址或者内核虚拟地址。如果你想告诉PCI卡DMA缓冲区的地址信息,必须让PCI卡知道DMA缓冲区的总线地址。总线地址的类型是dma_addr_t,在文件include/asm-your-arch/types.h里有定义

 

DMA还有几个概念要了解,一个是回弹缓冲区。回弹缓冲区驻留在可作为DMA缓冲区的内存区域里,在DMA请求的源或目的地址没有DMA功能的内存区域时,它可以作为临时内存区存放数据。例如,用DMA方式从32位PCI外围设备向地址高于4GB的目标传送数据时,如果没有IOMMU单元,就需要用到回弹缓冲区了。数据先暂时存放在回弹缓冲区,再复制到目标地址。第二个概念颇具DMA的特色:分散、聚集。当要传输的数据分布在不连续的内存上时,分散/聚集能对这些不连续缓冲区的数据进行一次性发送,反过来,DMA也能把数据从卡正确地传送到地址不连续的缓冲区中。分散/聚集通过减少重复的DMA传送请求来提高效率。

内核提供有用的API函数来屏蔽配置DMA的细节。如果你是在给支持总线管理的PCI卡(大多数PCI卡都支持)写驱动程序,这些API会更简单。PCI DMA函数基本上就是封装DMA的通用服务函数,在include/asm/-genetic/pci-dma-compat.h文件中有定义。本章中我们只用PCI的DMA API。

内核为PCI驱动程序提供了两类DMA服务

(1)一致性DMA访问方法。这些程序保证DMA传送数据的一致性。如果PCI设备和CPU都有可能干预DMA缓冲区,保证数据的一致性是很重要的,这时要用一致性API函数。它是性能上的一个折中。要得到能保证数据一致性的DMA缓冲区,用下面的API函数

 Void *pci_alloc_consistent(strcuct pci_dev *pdev,size_t  size,dma_addr_t *dma_handle);

 这个函数分配一个DMA缓冲区,生成它的总线地址,并返回相关的内核虚拟地址。前面两个参数是PCI设备结构体和DMA缓冲区的字节大小,第三个参数(dma-handle)是指向总线地址的指针,由函数调用产生。下面的代码片段分配和释放一个一致性DMA缓冲区

/*ALLOCATE*/

Void *vaddr = pci_alloc_consistent(pdev,size,&dma_handle)

/*free*/

Pci_free_consistent(pdev,size,vaddr,dma_handle);

(2)流式DMA访问函数。这些API不保证数据的一致性,所以速度更快。它们在不太需要CPU和I/O设备共享DMA缓冲区的时候比较有用。当设备存储的流式缓冲区被映射以便被设备访问后,驱动程序要明确得去映射(或同步)流式缓冲区,之后CPU才可以可靠地操作该缓冲区。流式访问有两类函数:pci_[map|unmap|dma_sync]_single()和pci_[map |unmap|dma_sync]_sg().

第一组函数可以映射、去映射以及同步一个预先分配的单一DMA缓冲区。Pci_map_single()的原型如下

Dma_addr_t pci_map_single(struct pci_dev *pdev,void *ptr,size_t size,int direction);

前三个参数分别表示PCI设备结构体,预先分配的DMA缓冲区虚拟内核地址和缓冲区的字节数。第四个参数取下面几个值:PCI_DMA_BIDIRECTION、PCI_DMA_TODEVICE、PCI_DMA_FROMDEVICE和PCI_DMA_NONE,从名字上就很容易看出这些变量的含义。但是使用第一个选项(PCI_DMA_BIDIRECTION)的代价比较大,最后一个选项是在调试的时候使用。

 

第二组函数映射、去映射并且同步一个分散/聚集的DMA缓冲区链。Pci_map_sg()的原型如下:

Int pci_map_sg(struct pci_dev *pdev,struct scatterlist *sgl,int num_entries,int direction);

第二个参数是sruct scatterlist表示分散内存的链表,在include/asm-your-arch/scatterlist.h

里面定义。Num_entries变量表示分散内存的链表入口函数。第一和最后一个参数的意思跟pci_map_single()函数里的参数是一样的。


     下面,我们来看一下8139too网卡设备的配置空间的详细情况。在2.6内核的系统中,可以在目录/sys/bus/pci/drivers/ 下看到很多以PCI设备名命名的目录,但不是说这些设备都存在于你的系统中。我们进入8139too目录,其中有一个以它的设备地址0000:02: 01.0命名的目录。在这个目录下可以找到该网卡设备相关的很多信息。其中resource记录了它的6个I/O地址区域。内容如下:
         0x0000000000003400 0x00000000000034ff 0x0000000000000101
         0x00000000e0000800 0x00000000e00008ff 0x0000000000000200
         0x0000000000000000 0x0000000000000000 0x0000000000000000
         0x0000000000000000 0x0000000000000000 0x0000000000000000
         0x0000000000000000 0x0000000000000000 0x0000000000000000
         0x0000000000000000 0x0000000000000000 0x0000000000000000
         0x0000000000000000 0x0000000000000000 0x0000000000000000
     由该文件可以看出,8139too设备使用了两个I/O地址区域,第一个是它映射的I/O端口范围,第二个是它映射的内存地址空间。关于这两个值可以在/proc/iomem和/proc/ioport中得到验证。

-[0000:00]-+-00.0
           +-02.0
           +-1d.0
           +-1d.1
           +-1d.2
           +-1d.7
           +-1e.0-[0000:01]--+-02.0
           |                 /-05.0
           +-1f.0
           +-1f.1
           +-1f.3
           /-1f.5
00:00.0 Host bridge: Intel Corporation 82845G/GL[Brookdale-G]/GE/PE DRAM Controller/Host-Hub Interface (rev 03)
00:02.0 VGA compatible controller: Intel Corporation 82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device (rev 03)
00:1d.0 USB Controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (rev 02)
00:1d.1 USB Controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (rev 02)
00:1d.2 USB Controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (rev 02)
00:1d.7 USB Controller: Intel Corporation 82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (rev 02)
00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 82)
00:1f.0 ISA bridge: Intel Corporation 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge (rev 02)

(LPC Hub 控制器 1 )
00:1f.1 IDE interface: Intel Corporation 82801DB (ICH4) IDE Controller (rev 02)
00:1f.3 SMBus: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (rev 02)
00:1f.5 Multimedia audio controller: Intel Corporation 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (rev 02)
01:02.0 Communication controller: Conexant HSF 56k HSFi Modem (rev 01)
01:05.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)

 

转载自:http://blog.chinaunix.net/uid-7374279-id-5765122.html 及https://blog.csdn.net/fight_onlyfor_you/article/details/76718741

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
安装lspci命令的步骤如下: 1. 首先,确定你的CentOS 7系统已启动并登录到终端。 2. 打开终端,并使用root权限登录。 3. 使用以下命令安装pciutils软件包:`yum install pciutils` 4. 安装完成后,你就可以在终端上使用lspci命令了,它会显示关于PCI设备的信息。 请注意,由于CentOS 7是一个基于Red Hat Enterprise Linux的发行版,因此步骤可能会与其他Linux发行版有所不同。为了确保安装成功,建议按照上述步骤进行操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [CentOS下安装lspci](https://blog.csdn.net/weixin_34272308/article/details/92453455)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [centos安装lspci工具](https://blog.csdn.net/wudiyi815/article/details/38325199)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [使用yum查看工具lspci所在包并安装的方法(详解)](https://download.csdn.net/download/weixin_38550334/14092427)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值