Linux设备驱动之pci设备的枚举

/* Configuration request Retry Status */ while (l == 0xffff0001) { msleep(delay); delay *= 2;

if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) return NULL;

/* Card hasn't responded in 60 seconds? Must be stuck. */ if (delay > 60 * 1000) {

printk(KERN_WARNING \ \ bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); return NULL; } }

从配置空间中读取该设备对应的vendor id和device id.如果读出来的值,有一个是空的,则说明该功能号对应的设备不存在,或者是配置非法.

如果读出来的是0xffff0001.则需要重新读一次,如果重读次数过多,也会退出

if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type)) return NULL;

dev = alloc_pci_dev(); if (!dev)

return NULL;

dev->bus = bus;

dev->sysdata = bus->sysdata; dev->dev.parent = bus->bridge; dev->dev.bus = &pci_bus_type; dev->devfn = devfn;

dev->hdr_type = hdr_type & 0x7f; dev->multifunction = !!(hdr_type & 0x80); dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; dev->cfg_size = pci_cfg_space_size(dev); dev->error_state = pci_channel_io_normal; set_pcie_port_type(dev);

/* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) set this higher, assuming the system even supports it. */ dev->dma_mask = 0xffffffff;

接着,将不同类型设备的共同头部配置读出来,然后赋值给pci_dev的相应成员.这里有个特别要值得注意的地方:

9

dev->dev.bus = &pci_bus_type.即将pci_dev里面封装的device结构的bus设置为了pci_bus_type.这个是很核心的一个步骤.我们先将它放到这里,之后的再来详细分析

特别的, HEADER_TYPE的最高位为0,表示该设备是一个单功能设备

if (pci_setup_device(dev) < 0) { kfree(dev); return NULL; }

return dev; }

最后,流程就会转入到pci_setup_deivce()对特定类型的设备配置都行读取操作了.代码如下: static int pci_setup_device(struct pci_dev * dev) {

u32 class;

sprintf(pci_name(dev), \ dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));

pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); dev->revision = class & 0xff;

class >>= 8; /* upper 3 bytes */ dev->class = class; class >>= 8;

pr_debug(\ dev->vendor, dev->device, class, dev->hdr_type);

/* \

dev->current_state = PCI_UNKNOWN;

/* Early fixups, before probing the BARs */ pci_fixup_device(pci_fixup_early, dev); class = dev->class >> 8;

switch (dev->hdr_type) { /* header type */

case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) goto bad; pci_read_irq(dev);

pci_read_bases(dev, 6, PCI_ROM_ADDRESS);

pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);

10

/*

* Do the ugly legacy mode stuff here rather than broken chip * quirk code. Legacy mode ATA controllers have fixed * addresses. These are not always echoed in BAR0-3, and * BAR0-3 in a few cases contain junk! */

if (class == PCI_CLASS_STORAGE_IDE) { u8 progif;

pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); if ((progif & 1) == 0) {

dev->resource[0].start = 0x1F0; dev->resource[0].end = 0x1F7;

dev->resource[0].flags = LEGACY_IO_RESOURCE; dev->resource[1].start = 0x3F6; dev->resource[1].end = 0x3F6;

dev->resource[1].flags = LEGACY_IO_RESOURCE; }

if ((progif & 4) == 0) {

dev->resource[2].start = 0x170; dev->resource[2].end = 0x177;

dev->resource[2].flags = LEGACY_IO_RESOURCE; dev->resource[3].start = 0x376; dev->resource[3].end = 0x376;

dev->resource[3].flags = LEGACY_IO_RESOURCE; } } break;

case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ if (class != PCI_CLASS_BRIDGE_PCI) goto bad;

/* The PCI-to-PCI bridge spec requires that subtractive decoding (i.e. transparent) bridge must have programming interface code of 0x01. */ pci_read_irq(dev);

dev->transparent = ((dev->class & 0xff) == 1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); break;

case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ if (class != PCI_CLASS_BRIDGE_CARDBUS) goto bad; pci_read_irq(dev); pci_read_bases(dev, 1, 0);

11

pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device); break;

default: /* unknown header */

printk(KERN_ERR \ pci_name(dev), dev->hdr_type); return -1;

bad:

printk(KERN_ERR \ pci_name(dev), class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; }

/* We found a fine healthy device, go go go... */ return 0; }

总共有三种类型的设备,分别为常规设备(PCI_HEADER_TYPE_NORMAL) ,pci-pci桥设备(PCI_HEADER_TYPE_BRIDGE),笔记本电脑上使用的cardbus(PCI_HEADER_TYPE_CARDBUS).这里的操作不外乎是IRQ的确定,设备存储区间映射等.先将这几个操作分析如下: 1: IRQ号的确定

该操作接口为pci_read_irq():

static void pci_read_irq(struct pci_dev *dev) {

unsigned char irq;

pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); dev->pin = irq; if (irq)

pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); dev->irq = irq; }

在PCI_INTERRUPT_PIN中存放的是将INTA~INTD的哪一个引脚连接到了中断控制器,如果该值为零.说明并末将引脚连接至中断控制器.自然也就不能产生中断信号.

其实,在PCI_INTERRUPT_LINE存放的是该设备的中断线连接在中断控制器的哪一个IRQ线上.也就是对应设备的IRQ.

注意这里的寄存器只读有意义,并不是更改寄存器的值就更改该设备的IRQ

2:内部存储区间的确定

从之前的pci设备配置寄存器图中可以看到.有从0x10~0x27的6个base address寄存器.里面存放的就是内部存储器的地起地址和长度,及其类型.

首先将对应寄存器的值取出.如果最低位为1.则说明该区域是I/O端口,高29位是端口地址的高29位,低3位为零.否则是存储映射区间.前28位是存储区的高28位,低四位为零.

12

联系客服:779662525#qq.com(#替换为@)