PCI设备驱动(二)

Linux PCI设备驱动代码必须扫描系统中所有的PCI总线,寻找系统中所有的PCI设备(包括PCI-PCI桥设备)。系统中的每条PCI总线都有个编号number,根PCI总线的编号为0。

一、总线链表

1、根总线链表( pci_root_buses )

  • 系统当前存在的所有根总线(因为可能存在不止一个Host/PCI桥,那么就可能存在多条根总线) 都通过其pci_bus结构体中的node成员链接成一个全局的根总线链表,其表头由struct list_head类型的全局变量pci_root_buses描述。

我们在/linux-2.4.18/linux/drivers/pci/pci.c的38行可以看到如下定义:LIST_HEAD(pci_root_buses);

2、子总线链表(children)

  • 根总线下面的所有下级总线则都通过其pci_bus结构体中的node成员链接到其父总线的children链表中。

这样,通过这两种PCI总线链表,Linux内核就将所有的pci_bus结构体以一种倒置树的方式组织起来。

二、PCI设备链表

每个PCI设备都由一个pci_dev结构体表示,每个pci_dev结构体都同时连入两个队列:

  • 一方面通过其成员global_list挂入一个总的pci_dev结构队列(队列头是pci_devices);
  • 另一方面又通过成员bus_list挂入其所在总线的pci_dev结构队列devices(队列头是pci_bus.devices,即该pci设备所在的pci总线的devices队列),并且使指针bus(指pci_dev结构体里的bus成员)指向代表着其所在总线的pci_bus结构。
  • 如果设备是PCI-PCI桥,则还要使其指针subordinate指向代表着另一条PCI总线的pci_bus结构。同样我们在/linux-2.4.18/linux/drivers/pci/pci.c的39行可以看到如下定义:LIST_HEAD(pci_devices);

对于PCI设备链表,我们可以对照在"PCI设备驱动(二)"中贴出的PCI系统结构示意图,具体理解Linux内核中对应的数据结构。
图片发自简书App

三、PCI驱动程序初始化

1、深度优先算法构建设备树

Linux PCI初始化代码从PCI总线0开始扫描,它通过读取"Vendor ID"和"Device ID"来试图发现每一个插槽上的设备。

  • 如果发现了一个PCI-PCI桥,则创建一个pci_bus数据结构并且连入到由pci_root_buses指向的pci_bus和pci_dev数据结构组成的树中。PCI初始化代码通过设备类代码0x060400来判断一个PCI设备是否是PCI-PCI桥。

  • 然后Linux核心开始构造这个桥设备另一端的PCI总线和其上的设备。如果还发现了桥设备,就以同样的步骤来进行构建。
    这个处理过程称之为深度优先算法。

PCI-PCI桥横跨在两条总线之间,寄存器PCI_PRIMARY_BUS和PCI_SECONDARY_BUS的内容就说明了其上下两端的总线号:

  • PCI_SECONDARY_BUS就是该PCI-PCI桥所连接和控制的总线,
  • PCI_SUBORDINATE_BUS则说明自此以下、在以此为根的子树中最大的总线号是什么。
    我们可以在/linux-2.4.18/linux/include/linux/pci.h看到如下定义:
112:  /* Header type 1 (PCI-to-PCI bridges) */

113:  #define PCI_PRIMARY_BUS           0x18       /* Primary bus number */

114:  #define PCI_SECONDARY_BUS     0x19       /* Secondary bus number */

115:  #define PCI_SUBORDINATE_BUS  0x1a       /* Highest bus number behind the bridge */

2、内核对设备的寻址方式

  • 由于在枚举阶段做的是深度优先扫描,所以子树中的总线号总是连续递增的。当CPU往I/O寄存器0xCF8中写入一个综合地址以后,从0号总线开始,每个PCI-PCI桥会把综合地址中的总线号与自身的总线号相比,如果相符就用逻辑设备号在本总线上寻访目标设备;

  • 如果综合地址中的总线号与自己不相符,就进一步把这个总线号与PCI_SUBORDINATE_BUS中的内容相比,如果目标总线号落在当前子树范围中,就把综合地址传递给其下的各个次层PCI-PCI桥,要不然就不予理睬。
    这样,最终就会找到目标设备。

当然,这个过程只是在PCI设备的配置阶段需要这样做,一旦配置完成,CPU就直接通过有关的总线地址访问目标设备了。

3、PCI桥的窗口

PCI-PCI桥要想正确传递对PCI I/O,PCI Memory或PCI Configuration地址空间的读和写请求,必须知道下列信息:

  1. Primary Bus Number(主总线号)
    该PCI-PCI桥所处的PCI总线称为主总线。
  2. Secondary Bus Number(子总线号)
    该PCI-PCI桥所连接的PCI总线称为子总线/次总线号。
  3. Subordinate Bus Number
    PCI总线的下属PCI总线的总线编号最大值。

(1)什么是PCI桥窗口

PCI桥的配置寄存器与一般的PCI设备不同。

  • 一般PCI设备可以有6个地址区间,外加一个ROM区间,代表着设备上实际存在的存储器或寄存器区间。
  • PCI桥设备,本身并不一定有存储器或寄存器区间,但是却有三个用于地址过滤的区间。每个地址过滤区间决定了一个地址窗口,从CPU一侧发出的地址,如果落在PCI桥的某个窗口内,就可以穿过PCI桥而到达其所连接的总线上。

(2)PCI桥窗口的开关

  • PCI桥的命令寄存器中有”memory access enable”和”I/O access enable”的两个控制位,当这两个控制位为0时,这些窗口就全都关上了。

(3)PCI桥窗口的作用

在未完成对PCI总线的初始化之前,还没有为PCI设备上的各个区间分配合适的总线地址时,正是因为这两个控制位为0,才不会对CPU一侧造成干扰。

  • 对于PCI设备驱动(一)中的 PCI系统示意图 ,仅当读和写请求中的PCI I/O或PCI memory地址属于SCSI或Ethernet设备时,PCI-PCI桥才将这些总线上的请求从PCI总线0传递到PCI总线1。这种过滤机制可以避免地址在系统中没必要的繁衍。
  • 为了做到这点,每个PCI-PCI桥必须正确地被设置好它所负责的PCI I/O或PCI memory的起始地址和大小。当一个读或写请求地址落在其负责的范围之内,这个请求将被映射到次级的PCI总线上。系统中的PCI-PCI桥一旦设置完毕,如果Linux中的设备驱动程序存取的PCI I/O和PCI memory地址落在在这些窗口之内,那么这些PCI-PCI桥就是透明的。
  • 这是个很重要的特性,使得Linux PCI设备驱动程序开发者的工作容易些。

4、PCI桥的窗口的配置

问题:配置一个PCI-PCI桥的时候,并不知道这个PCI-PCI桥的subordinate bus number。那么就不知道该PCI桥下面是否还有其他的PCI-PCI桥。即使你知道,也不清楚如何对它们赋值。

方法:利用上述的深度扫描算法来扫描每个总线。每当发现PCI-PCI桥就对它进行赋值。当发现一个PCI-PCI桥时,可以确定它的secondary bus number。然后我们暂时先将其subordinate bus number赋值为0xFF。紧接着,开始扫描该PCI-PCI桥的downstream桥。

过程:这个过程看起来有点复杂,下面的例子将给出清晰的解释:

(1)第一步

以下图的拓扑结构为例:

  • 扫描时首先发现的桥是Bridge1。Bridge 1的downstream PCI总线号码被赋值1。自然该桥的secondary bus number也是1。其subordinate bus number暂时赋值为0xFF。上述赋值的含义是所有类型1的含有PCI总线1或更高(<255)的号码的PCI配置地址将被Bridge 1传递到PCI总线1上。
  • 如果PCI总线号是1,Bridge 1 还负责将配置地址的类型转换成类型0(对于这里说的类型0和类型1,请参考PCI设备驱动(一))。否则,就不做转换。上述动作就是开始扫描总线1时Linux PCI初始化代码所完成的对总线0的配置工作。
    配置PCI系统 第一步

(2)第二步

  • 由于Linux PCI设备驱动使用深度优先算法进行扫描,所以初始化代码开始扫描总线1。从而Bridge 2被发现。
  • 因为在Bridge 2下面发现不再有PCI-PCI桥,所以Bridge 2的subordinate bus number是2,等于它的secondary bus number。下图显示了在这个时刻总线和PCI-PCI桥的赋值情况。
    配置PCI系统 第二步

(3)第三步

  • Linux PCI设备驱动代码从总线2的扫描中回来接着进行扫描总线1,发现Bridge 3。
  • 它的primary bus number被赋值为1,secondary bus number为3。因为总线3上还发现了PCI-PCI桥,所以Bridge 3的subordinate bus number暂时赋值0xFF。
  • 下图显示了这个时刻系统配置的状态。到目前为止,含有总线号1,2,3的类型1的PCI配置都可以正确地传送到相应的总线上。
    配置PCI系统 第三步

(4)第四步

  • 现在Linux开始扫描PCI总线3,Bridge 3的downstream。PCI总线3上有另外一个PCI-PCI桥,Bridge 4。因此Bridge 4的primary bus number的值为3,secondary bus number为4。
  • 由于Bridge 4下面没有别的桥设备,所以Bridge 4的subordinate bus number为4。
  • 然后回到PCI-PCI Bridge 3。这时就将Bridge 3的subordinate bus number从0xFF改为4,表示总线4是从Bridge 3往下走的最远的PCI-PCI桥。
  • 最后,Linux PCI设备驱动代码将4以同样的道理赋值给Bridge 1的subordinate bus number。下图反映了系统最后的状态。
    配置PCI系统 第四步
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 首先要明确两个概念:Linux内核 PCI设备驱动和设备本身驱动两部分。工作中所谓的编写设备驱动,其实就是编写设备...
    Leon_Geo阅读 3,243评论 0 6
  • 总线 计算机的各个功能部件通过总线连接在一起构成完整的计算机系统,总线是多个系统功能部件之间进行数据传送的公共通路...
    罗蓁蓁阅读 4,790评论 0 10
  • (转)从PC总线到ARM的内部总线转自:http://blog.chinaunix.net/u1/34474/sh...
    spfanlost阅读 1,426评论 0 2
  • 昨晚,在大学班级微信群里发现一个噩耗,一大学同学被酒驾的司机撞到,因肇事司机逃逸没有及时报警,同学不治身亡。他的爱...
    黄家小妞阅读 120评论 0 0
  • 01 与邓先生的相识回忆起来极其漫长,在一起之前是一个素未谋面的人。或许是我喜欢有一点小腱子肉的男生的原因,看到他...
    一只小桃子阅读 443评论 5 2