基于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. fuzz系列之afl

    afl 实战 前言 像 libFuzzer, afl 这类 fuzz 对于 从文件 或者 标准输入 获取输入的程序都能进行很好的 fuzz, 但是对于基于网络的程序来说就不是那么方便了. 这篇文章介绍 ...

  2. 格式化字符串漏洞利用实战之 njctf-decoder

    前言 格式化字符串漏洞也是一种比较常见的漏洞利用技术.ctf 中也经常出现. 本文以 njctf 线下赛的一道题为例进行实战. 题目链接:https://gitee.com/hac425/blog_d ...

  3. 从源码上分析ListView的addHeaderView和setAdapter的调用顺序

    ListView想要添加headerview的话,就要通过addHeaderView这个方法,然后想要为ListView设置数据的话,就要调用setAdapter方法了.但是,在调用addHeader ...

  4. 解决python2和python3的pip冲突

    最近突然出现了一种情况当电脑上同时安装python2和python3的时候会导致我的pip冲突 . 最终经过我的发现是因为其环境没有配置好 还有就是没有找到精准的包导致的 1.下载python2.7, ...

  5. [翻译] LiquidFloatingActionButton

    LiquidFloatingActionButton https://github.com/yoavlt/LiquidFloatingActionButton LiquidFloatingAction ...

  6. Session管理

    request.session.set_expiry(10) #设置10s后session失效request.session.get_expire_at_browser_close() #查看sess ...

  7. The expression of type Integer is unboxed into int

    问题:The expression of type Integer is unboxed into int 原因:java的包装类,方法里面要的是Integer,传入的参数确实int类型 解决方案: ...

  8. Laravel 实践之路: 数据库迁移与数据填充

    数据库迁移实际上就是对数据库库表的结构变化做版本控制,之前对数据库库表结构做修改的方式比较原始,比如说对某张库表新增了一个字段,都是直接在库表中执行alter table xxx add .. 的方式 ...

  9. 使用FASTJSON做反序列化的时间格式处理

    JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.mmm"; Productorder tmp1 = JSONObj ...

  10. 微软YY公开课[《微软中国云计算Azure平台体验与新企业架构设计》 周六晚9点

    YY频道是 52545291//@_勤_: YY账号真的是一次一账号啊! 全然记不得之前注冊的//@老徐FrankXuLei: 最火爆的微软免费公开课.第一次顶峰126人.第二次96人.第三次我们又来 ...