pci枚举初始化部分(1)
基于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, ®16);
pdev->pcie_flags_reg = reg16;
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, ®16);
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)的更多相关文章
- pci枚举初始化部分(2)
1.2.8判断pcie设备是否支持雷电技术 Intel具有一种基于Thunderbolt技术的PCIE变体,它结合了DisplayPort和PCIe协议,与Mini DisplayPort兼容. Th ...
- linux PCI设备初始化过程
linux PCI设备初始化过程 start_kernel->rest_init 这个函数会启动一个核心线程0, 核心线程然后调用init -> do_basic_setup. 然后我们开 ...
- 【转】PCI学习笔记
1.PCI设备编号 每一个PCI device都有其unique PFA(PCI Fcntion Address) PFA由 bus number.device number.functi ...
- 【原创】Linux PCI驱动框架分析(二)
背 景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本 ...
- Swift中如何化简标准库中冗长的类实例初始化代码
可能有些童鞋并不知道,在Swift中缩写点符号对于任何类型的任何static成员都有效. 我们实际写一个例子看一下: import UIKit class CFoo{ static let share ...
- Kotlin 枚举类
枚举类最基本的用法是实现一个类型安全的枚举. 枚举常量用逗号分隔,每个枚举常量都是一个对象. enum class Color{ RED,BLACK,BLUE,GREEN,WHITE } 枚举初始化 ...
- [知乎]老狼:深入PCI与PCIe之二:软件篇
深入PCI与PCIe之二:软件篇 https://zhuanlan.zhihu.com/p/26244141 我们前一篇文章(深入PCI与PCIe之一:硬件篇 - 知乎专栏)介绍了PCI和PCIe的硬 ...
- Swift_初始化
#Swift_初始化 点击查看源码 初始化结构体 //初始化结构体 func testInitStruct() { //结构体 类中默认方法 struct Size { //宽 var width = ...
- java编程思想第五章初始化与清理
5.1使用构造器确保初始化: 构造器与一般方法一样,但是没有返回值,且其方法名与类名完全相同. 不接受任何参数的构造器成为默认构造器,也叫无参构造器. 5.2 方法重载: 为什么会有方法重载? 构造器 ...
随机推荐
- idapython 开发
调试方法 使用 pydevd 然后在需要调试处加入调试代码 GetOperandValue 作用 参数1: ea 虚拟地址 参数2: 操作数号 返回指令的操作数的被解析过的值 文档 def GetOp ...
- vue图片上传及java存储图片(亲测可用)
1.前言 在使用elementui的upload组件时,我一直无法做到上传的图片和其他数据一起提交.单纯的上传文件,java的存储图片的方式也有局限性. 我知道的后端保存图片有两种方式:一种是直接存储 ...
- Vue 框架-07-循环指令 v-for,和模板的使用
Vue 框架-07-循环指令 v-for,和模板的使用 本章主要是写一些小实例,记录代码,想要更详细的话,请查看 官方文档:https://cn.vuejs.org/v2/guide/#%E6%9D% ...
- mysql 日期时间类型
datetime timestamp year date time drop table test;create table test (dt datetime, ts timestamp, y ye ...
- MQTT介绍(1)简单介绍
MQTT目录: MQTT简单介绍 window安装MQTT服务器和client java模拟MQTT的发布,订阅 MQTT: MQTT(Message Queuing Telemetry Transp ...
- Oracle EBS 查询物料报错
- MACOS root密码忘了怎么办?
MAC中ROOT用户很少用到,有时又手贱,给设了密码,过一阵忘了密码,很尴尬
- Git修改子模块的路径
Git在两个地方存储有关子模块的信息.第一个是在一个名为的文件中.gitmodules,该文件被签入git存储库.对此文件的更改将传播到其他存储库. 另一个位置在.git/config,并且它是执行大 ...
- amazon interview
I'll be sitting for an Amazon interview in 3 months. Which website should I use to practice: SPOJ, H ...
- PHP利用二叉堆实现TopK-算法的方法详解
前言 在以往工作或者面试的时候常会碰到一个问题,如何实现海量TopN,就是在一个非常大的结果集里面快速找到最大的前10或前100个数,同时要保证 内存和速度的效率,我们可能第一个想法就是利用排序,然后 ...