Linux设备驱动程序——PCI相关知识的学习

PCI相关的知识的学习

一、首先了解一下PCI相关的一些基础知识:

PCI总线的结构:


PCI总线结构是一种层次型的体系结构,在这个结构体中PCI桥设备占据重要的地位。将父总线和子总线连接在一起,使得整个系统像一棵倒置的树。PCI桥主要包括以下几种:

Host/PCI桥:用于连接CPUPCI根总线,第一根的总线为0,内存控制器通常也集成到这个桥设备中。通常也被称为北桥芯片组。

PCI/ISA桥:用于连接旧的ISA总线。

PCI-to-PCI 桥:用于连接 PCI 主总线和次总线

PCI中常用到几个的结构体类型

PCI中牵涉到很多结构体类型主要有:pci_buspci_driverpci_device_idpci_dev、这些结构体的成员和相关的作用放在我的笔记中。

 

PCI中用到的三种地址空间

PCI中有三种空间:PCI I/O空间、PCI内存空间和PCI地址空间。下面主要学一下PCI配置空间关于I/O空间和内存空间的内容在后面部分再详细学习。其中I/O空间和内存空间给驱动使用,而配置空间用来给内核中的初始化部分使用。例如中断号,或者I/O或者内存的基地址

下面先看一下PCI配置寄存器:

由于PCI支持设备即插即用,所以PCI设备不占用固定的内存地址空间或I/O地址空间,而是由操作系统决定其映射的基址。

系统加电时,BIOS检测PCI总线,确定所有连接在PCI总线上的设备以及它们的配置要求,并进行系统配置。所以,所有的PCI设备必须实现配置空间,从而能够实现参数的自动配置,实现真正的即插即用。

PCI总线可以通过桥芯片级连.按与CPU总线所隔的桥数目和同一层总线的扫描顺序,0开始依次编号,扩展最多N2560—256就是总线号;在指定的局部总线上.按硬件扫描顺序从O开始依次编号,扩展最多到32032就是设备号;在一个多功能PCI设备上,最多可以实现8种功能,按设备上配置存储区的顺序从0开始依次编号,称其为功能号,一般的设备只有一个功能这三个号组合起来就可确定唯一的一个PCI设备。以及该设备上的某项功能。通过这三个参数(可组成PCI设备标识符)就可操作PCI设备。

PCI设备有一个256字节的地址空间,前64个字节是标准化的,其余的则是与设备相关的

 

 

vendorID:这个 16-位 寄存器标识一个硬件制造商.。例如, 每个 Intel 设备都标有相同的供应商号,,0x8086. 这样的号有一个全球的注册,,由 PCI 特别利益体所维护, 并且供应商必须申请有一个唯一的分配给它们的号。

deviceID:这是另一个 16-位寄存器, 由供应商选择; 对于这个设备 ID 没有要求官方的注册. 这个 ID常常和 供应商 ID 成对出现来组成一个唯一的 32-位标识符给一个硬件设备. 我们使用词语"签名"来指代供应商和设备 ID 对.一个设备驱动常常依靠签名来标识它的设备; 你可在硬件手册中找到对于目标设备要寻找的值。

Class:每个外设都属于一个类. 类寄存器是一个 16-位 值, 它的高 8 位标识"基类"(或者群).例如, "ethernet"和"tokenring"是 2 个类都属于"network"群, 而"serial"和"parallel"属于"communication"群. 一些驱动可支持几个类似的设备,每个都有一个不同的签名但是都属于同样的类; 这些驱动可依赖类寄存器标识它们的外设。

 

每个PCI设备在配置空间中固化了自己的基本信息,内核启动的时候PCI总线驱动程序会把这些信息收集起来,保存在pci_dev上面。Pci_register会将pci_dev_id列表中的信息和内核扫描到的进行对比,当发现有合适的时候就会将驱动程序和目标设备联系起来。

 

read()和write()成员函数中的size 表示访问的是字节、2  字节还是4  字节,对于 write()而言,val 是要写入的值;对于read()而言,val 是要返回的读取到的值的指针。 通过bus 参数的成员以及devfn 可以定位相应PCI 总线上相应PCI 逻辑设备的配置空 间。在Linux 设备驱动中,可用如下一组函数来访问配置空间:

从由 dev 所标识出的设备的配置空间读 1 个, 2 个或者 4 个字节.where 参数是从配置空间开始的字节偏移. 从配置空间取得的值通过 val  指针返回, 并且这个函数的返回值是一个错误码. word 和 dword 函数转 换刚刚读的值从小端到处理器的本地字节序, 因此你不必处理字节序.

inline int pci read configbyte(struct pci dev *dev, int where, u8 *val); 

inline int pci read configword(struct pci dev *dev, int where, u16 *val); 

inline int pci read configdword(struct pci dev *dev, int where, u32 *val); 

写 1 个、2 个或者 4 个字节到配置空间. 象通常一样, 设备由 dev 所标 识, 并且象通常一样被写的值被传递. word 和 dword 函数转换这个值到 小端, 在写到外设之前。
inline int pci write config byte(struct pci dev *dev, int where, u8 val); 
    inline int pci write configword(struct pci dev *dev, int where, u16 val); 
    inline int pci write configdword(struct pci dev *dev, int where, u32 val);

上述函数只是对如下函数进行调用: 
int pci bus read config byte (struct pci bus *bus, unsigned int devfn, intwhere, u8 *val); //读字节  devfn为PCI设备的设备功能号
int pci bus read config word (struct pci bus *bus, unsigned int devfn, intwhere, u16 *val); //读字 
intpci bus read config dword (structpci bus *bus,unsigned int devfn, int where,u32 *val); //读双字 
intpci bus write config byte (structpci bus *bus,unsigned int devfn, int where,u8 val); //写字节 
intpci bus write config word(structpci bus *bus,unsigned int devfn, int where, u16 val); //写字 
intpci bus write config dword (structpci bus *bus,unsigned int devfn, intwhere, u32 val); //写双字

 

访问I/O和内存空间(关于这部分内容还有很多不清楚的地方,当学习完内存和I/O部分之后再回头来补充这部分的内容。)

 

PCI中断

 

计算机固件已经给设备分配了一个唯一的中断号,驱动程序只需要使用它即可,中断号放在配置寄存器60中(PCI_INTERUPT_LINE)中,这是一个字节的宽度,最多云允许256个中断线。

    如果设备不支持中断,寄存器61中(PCI_INTERUPT_PIN)为0;不然的话为非0;

 

大蓝图

 

下面来总体上了解一下PCI总线系统在系统引导期间会发生什么样的事情

 

当系统引导的时候,会建立一种数据库,把每个总线都关联一份已经侦查到的设备列表。当设备驱动程序加载的时候,内核已经建立好了数据库

以下图有三个PCI设备的为例来看一下,当驱动程序A和驱动程序B加载的时候会发生什么样的情况。


 

当设备驱动程序A被加载的时候,会调用pci_regesier_driver并且提供pci_driver实例与PCI层注册,pci_driver结构体中会含有一个此pci设备ID的向量。然后PCI层区查看在已侦测到的PCI设备列表中与那些设备匹配,于是就会建立该驱动程序的设备列表,如下图所示。对于每个匹配的设备而言,PCI层会调用pci_driver中的probe函数,probe函数会建立并且注册相关联的网络设备。当驱动程序B终于与内核注册后,DEV3就会被分配给驱动程序B,最后一幅图就是现实加载了驱动程序后的效果。

 

    相反的,当驱动成稍后卸载的时候,该模块的moudule_exit函数就会调用pci_unregester_driver,使得PCI能够遍历所有与该驱动程序相关的设备,并且启用该驱动程序的remove函数,此函数就会将此网络设备除名。



补充:在经过前一段的学习中,在看新的代码中的过程中又发现了新的问题,在这里将提出的问题写下来,并且将自己通过查找资料解决写下来。

1、PCI寻址和总线结构

每个 PCI 外设有一个总线号, 一个设备号, 一个功能号标识. PCI 规范允许单个系统占用多达 256 个总线,每个总线占用 32 个设备,每个设备可以是

一个多功能卡(例如一个声音设备, 带有一个附加的 CD-ROM 驱动)有最多 8 个功能,桥是特殊用途的PCI外设,用来连接两个总线。

关于PCI总线的信息可以用 cat  /proc/bus/pci/devices |cut -f1  命令或者通过 tree /sys/bus/pci/devices,但是在我用的机器上没有tree命令。

下面讲一下和PCI相关的16位硬件地址,通常情况下看到的是3个值(8位的总线号,5位的设备号,3位的功能号),距离如下:

 那上述的这个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区已经被集成到通用的资源管理器中,由于这个原因,不用存取配置变量来知道设备映射到内存或者I/O空间的什么地方,可以用下面一些函数来查看被映射到内存的什么地方。

unsigned long pci_resource_start(struct pci_dev *dev, int bar);

这个函数返回第一个地址(内存地址或者 I/O 端口号), 和 6 个 PCI I/O 区中的一个相关联的. 这个区通过整数 bar (the base address register), 范围从 0-5
(包含).

unsigned long pci_resource_end(struct pci_dev *dev, int bar);

这个函数返回最后一个地址, I/O 区号 bar 的一部分. 注意这是最后一个可用地址, 不是这个区后的第一个地址.

unsigned long pci_resource_flags(struct pci_dev *dev, int bar);

这个函数返回和这个资源相关联的标识。

所有的资源标志都定义在 <linux/ioport.h>,比较重要的是:

IORESOURCE_IO

IORESOURCE_MEM

如果被关联的I/O区存在,一个并且只有一个这样的标志被设置

IORESOURCE_PREFETCH

IORESOURCE_READONLY

其他I/O相关的标志放在笔记中,也可以从源代码中来看。







  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值