基于linux-4.20-rc3源码分析

1 .扫描所有PCI设备并检测,填充设备结构体

static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
u32 l; //查询PCI设备厂商号和设备号,以判断设备是否发生异常
if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
return NULL; //分配设备结构体
dev = pci_alloc_dev(bus);
if (!dev)
return NULL; dev->devfn = devfn;
dev->vendor = l & 0xffff; //获取厂商号
dev->device = (l >> 16) & 0xffff; //获取设备号 //设置链表节点
pci_set_of_node(dev); //扫描所有设备并设置,和获取信息
if (pci_setup_device(dev)) {
pci_bus_put(dev->bus);
kfree(dev);
return NULL;
} return dev;
}

其中pci_setup_device(dev)函数对挂载在该总线上所有的设备进行检测并获取相关数据,并设备信息进行填充。对于有些需特殊处理的设备也进行了特殊处理,达到尽量兼容新老设备的目的。

1.1查询设备厂商号和设备号

pci_bus_read_dev_vendor_id()

#ifdef CONFIG_PCI_QUIRKS
struct pci_dev *bridge = bus->self; /*
* Certain IDT switches have an issue where they improperly trigger
* ACS Source Validation errors on completions for config reads.
*/
//某些IDT交换机有一个问题,即它们在完成配置读取时错误地触发ACS源验证错误。
if (bridge && bridge->vendor == PCI_VENDOR_ID_IDT &&
bridge->device == 0x80b5)
return pci_idt_bus_quirk(bus, devfn, l, timeout);
#endif return pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);

该函数主要读取PCI设备的厂商号和设备号,如果读取出来包含全0,全1之类数据,这判断为PCI设备异常不再进行下一步操作。

对于某些IDT交换机设备,可能存在在读取设备信息时触发ACS验证错误,此时需要进行特殊操作,如果代码允许在IDT交换机上请一定配置PCI_QUIRKS选项。

ACS:安全接入控制器,接入服务器(Access Server)又称网络接入服务器NAS或远程接入服务器RAS,它是位于公用电话网(PSTN/ISDN)与IP网之间的一种远程访问接入设备。

  • pci_bus_generic_read_dev_vendor_id()

    该函数用于获取设备厂商号,并对错误的状态进行识别,对需重试获取的设备进行重试获取信息,并通过超时加以限制。
 if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
return false; /* Some broken boards return 0 or ~0 if a slot is empty: */
//如果槽为空时会返回0或者~0
if (*l == 0xffffffff || *l == 0x00000000 ||
*l == 0x0000ffff || *l == 0xffff0000)
return false; //具有配置重试机制的设备进行重试读取厂商号
if (pci_bus_crs_vendor_id(*l))
return pci_bus_wait_crs(bus, devfn, l, timeout); return true;

1.2获取设备信息并设置

1.2.1获取设备头信息

pci_hdr_type()

 pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);

1.2.2 判断是否为pcie设备

//判断是否为pcie设备
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!pos)
return; pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
pdev->pcie_flags_reg = reg16;
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;

通过state寄存器获取capability的有效性,并通过capability首地址顺势查找是否有PCIE capatility结构体存在,从而判断该设备是否时pcie设备

1.2.2.1 查找设备的capability,判断其是否为pcie设备

pci_find_capability()

//判断capability有效性,有效则获取capability表起始地址
pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type); //获取ID为cap的capability的偏移
if (pos)
pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);
  • __pci_bus_find_cap_start()
 //判断capabilityPointer寄存器中的值是否有效
pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status);
if (!(status & PCI_STATUS_CAP_LIST))
return 0; switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE:
return PCI_CAPABILITY_LIST; //普通设备偏移
case PCI_HEADER_TYPE_CARDBUS:
return PCI_CB_CAPABILITY_LIST; //桥设备偏移
}
  • __pci_find_next_cap()
//pos为capatibility结构首地址
pci_bus_read_config_byte(bus, devfn, pos, &pos); //目前ttl = PCI_FIND_CAP_TTL = 48;
while ((*ttl)--) {
if (pos < 0x40)
break;
pos &= ~3;
pci_bus_read_config_word(bus, devfn, pos, &ent); id = ent & 0xff;
if (id == 0xff)
break;
if (id == cap) //获取ID为PCI_CAP_ID_EXP的capatility结构体偏移
return pos;
pos = (ent >> 8);
}

1.2.3 添加槽结构体到链表

pci_dev_assign_slot()

 struct pci_slot *slot;

 mutex_lock(&pci_slot_mutex);
list_for_each_entry(slot, &dev->bus->slots, list)
if (PCI_SLOT(dev->devfn) == slot->number)
dev->slot = slot;
mutex_unlock(&pci_slot_mutex);

1.2.4设置驱动名称

 dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
dev->bus->number, PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));

1.2.5获取设备类别等信息

//获取设备类别
class = pci_class(dev); //读取PCI空间偏移0x8处数据。 dev->revision = class & 0xff; //版本号
dev->class = class >> 8; /* upper 3 bytes */ //设备类型

1.2.6 boot阶段打印PCI信息

if (pci_early_dump)

early_dump_pci_device(dev);

boot阶段用于打印pci设备所有配置空间信息

1.2.7 获取pci配置空间大小

pci_cfg_space_size()

通过判断设备类型从而获取配置空间大小。

pci和pxi模式1的设备的配置空间大小为256byte,PXI模式2和pcie设备的配置空间大小为4096byte

pci枚举初始化部分(1)的更多相关文章

  1. pci枚举初始化部分(2)

    1.2.8判断pcie设备是否支持雷电技术 Intel具有一种基于Thunderbolt技术的PCIE变体,它结合了DisplayPort和PCIe协议,与Mini DisplayPort兼容. Th ...

  2. linux PCI设备初始化过程

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

  3. 【转】PCI学习笔记

    1.PCI设备编号    每一个PCI device都有其unique PFA(PCI Fcntion Address)    PFA由 bus number.device number.functi ...

  4. 【原创】Linux PCI驱动框架分析(二)

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

  5. Swift中如何化简标准库中冗长的类实例初始化代码

    可能有些童鞋并不知道,在Swift中缩写点符号对于任何类型的任何static成员都有效. 我们实际写一个例子看一下: import UIKit class CFoo{ static let share ...

  6. Kotlin 枚举类

    枚举类最基本的用法是实现一个类型安全的枚举. 枚举常量用逗号分隔,每个枚举常量都是一个对象. enum class Color{ RED,BLACK,BLUE,GREEN,WHITE } 枚举初始化 ...

  7. [知乎]老狼:深入PCI与PCIe之二:软件篇

    深入PCI与PCIe之二:软件篇 https://zhuanlan.zhihu.com/p/26244141 我们前一篇文章(深入PCI与PCIe之一:硬件篇 - 知乎专栏)介绍了PCI和PCIe的硬 ...

  8. Swift_初始化

    #Swift_初始化 点击查看源码 初始化结构体 //初始化结构体 func testInitStruct() { //结构体 类中默认方法 struct Size { //宽 var width = ...

  9. java编程思想第五章初始化与清理

    5.1使用构造器确保初始化: 构造器与一般方法一样,但是没有返回值,且其方法名与类名完全相同. 不接受任何参数的构造器成为默认构造器,也叫无参构造器. 5.2 方法重载: 为什么会有方法重载? 构造器 ...

随机推荐

  1. mybatis大于小于的转义

    最近在使用mybatis,然后用到了小于等于,直接在XML中使用了<=,结果XML文件一直显示红色错误,如下: sum(case when p.pool_year <= '2014' th ...

  2. 命令模式-实现undo和redo

    这次实验主要是实现多次redo和undo,即程序的撤回和恢复,这里只实现加法的撤回和恢复. 程序的撤回和恢复就是由所使用的软件来记录操作步骤,可以将数据恢复到某个操作状态. 撤回这个指令很常见,Win ...

  3. TestNG 判断文件下载成功

    用WatchService写一个方法放在onTestStart()方法里监听文件夹的变化. 但是判断下载成功还需要写一个方法, 用来判断什么时候文件从.xlsx.rcdownload改成.xlsx才行 ...

  4. selenium模拟鼠标操作

    Selenium提供了一个类ActionChains来处理模拟鼠标事件,如单击.双击.拖动等. 基本语法: class ActionChains(object): """ ...

  5. consul日常操作命令

    #开发模式运行agent consul agent -dev #查看consul 集群成员 consul members [-detailed] members命令的输出基于gossip协议,并最终一 ...

  6. 【matlab】 幂法 求解最大特征值

    一. 算法: 1.输入矩阵A,初始向量x误差限ep,最大迭代次数N 2.置 k = 1, m1 = 0; 3.求Xr-> norm(x)   abs(Xr)=max[Xi] 1<=i< ...

  7. gamit安装

    需要准备的文件: 默认已安装好虚拟机和Ubuntu系统 1.输入用户名密码,进入Ubuntu10.04桌面.按下“Ctrl+Alt+T”,进入终端: 2.在终端输入“sudo gedit /etc/a ...

  8. 线段树&&线段树的创建线段树的查询&&单节点更新&&区间更新

    目录 线段树 什么是线段树? 线段树的创建 线段树的查询 单节点更新 区间更新 未完待续 线段树 实现问题:常用于求数组区间最小值 时间复杂度:(1).建树复杂度:nlogn.(2).线段树算法复杂度 ...

  9. JVM 调优参数详解

    GC有两种类型:Scavenge GC 和Full GC 1.Scavenge GC 一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,堆的Eden区域进行GC, ...

  10. Center OS 7 /etc/rc.d/init.d/network, status=6

    service network restart 报错 Center OS 7 /etc/rc.d/init.d/network status=6 google上找到答案: Just in case a ...