基於linux 4.20 rc3源碼分析 1 .掃描所有PCI設備並檢測,填充設備結構體 其中pci_setup_device(dev)函數對掛載在該匯流排上所有的設備進行檢測並獲取相關數據,並設備信息進行填充。對於有些需特殊處理的設備也進行了特殊處理,達到儘量相容新老設備的目的。 1.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