Linux 启动代码 枚举,【linux源码解析】【linux4.x】pci枚举初始化部分(1) · 杨旭的博客...

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30static 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()1

2

3

4

5

6

7

8

9

10

11

12

13

14#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()

该函数用于获取设备厂商号,并对错误的状态进行识别,对需重试获取的设备进行重试获取信息,并通过超时加以限制。1

2

3

4

5

6

7

8

9

10

11

12

13

14if (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()1pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);

1.2.2 判断是否为pcie设备1

2

3

4

5

6

7

8

9

10//判断是否为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()1

2

3

4

5

6//判断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()1

2

3

4

5

6

7

8

9

10

11

12//判断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()1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17//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()1

2

3

4

5

6

7struct 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设置驱动名称1

2

3dev_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获取设备类别等信息1

2

3

4

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);

1.2.7 获取pci配置空间大小

pci_cfg_space_size()

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值