/* * Copyright (c) 2006-2010 Dell, Inc. * by Matt Domsch * Licensed under the GNU General Public license, version 2. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "pci.h" #include "sysfs.h" #include "dmidecode/dmidecode.h" #include "pirq.h" #ifndef PCI_CB_CAPABILITY_LIST #define PCI_CB_CAPABILITY_LIST 0x14 #endif /* Borrowed from kernel vpd code */ #define PCI_VPD_LRDT 0x80 #define PCI_VPD_SRDT_END 0x78 #define PCI_VPD_SRDT_LEN_MASK 0x7 #define PCI_VPD_LRDT_TAG_SIZE 3 #define PCI_VPD_SRDT_TAG_SIZE 1 #define PCI_VPD_INFO_FLD_HDR_SIZE 3 static inline u16 pci_vpd_lrdt_size(const u8 *lrdt) { return (u16)lrdt[1] + ((u16)lrdt[2] << 8L); } static inline u8 pci_vpd_srdt_size(const u8* srdt) { return (*srdt) & PCI_VPD_SRDT_LEN_MASK; } static inline u8 pci_vpd_info_field_size(const u8 *info_field) { return info_field[2]; } static int pci_vpd_size(struct pci_device *pdev, int fd) { uint8_t buf[3], tag; int off; if (!is_pci_network(pdev)) return 0; off = 0; for(;;) { if (pread(fd, buf, 1, off) != 1) break; if (buf[0] & PCI_VPD_LRDT) { tag = buf[0]; if (pread(fd, buf, 3, off) != 3) break; off += PCI_VPD_LRDT_TAG_SIZE + pci_vpd_lrdt_size(buf); } else { tag = buf[0] & ~PCI_VPD_SRDT_LEN_MASK; off += PCI_VPD_SRDT_TAG_SIZE + pci_vpd_srdt_size(buf); } if (tag == 0 || tag == 0xFF || tag == PCI_VPD_SRDT_END) break; } return off; } static int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt) { int i; for (i = off; i < len;) { u8 val = buf[i]; if (val & PCI_VPD_LRDT) { if (i + PCI_VPD_LRDT_TAG_SIZE > len) break; if (val == rdt) return i; i += PCI_VPD_LRDT_TAG_SIZE + pci_vpd_lrdt_size(&buf[i]); } else { u8 tag = val & ~PCI_VPD_SRDT_LEN_MASK; if (tag == rdt) return i; if (tag == PCI_VPD_SRDT_END) break; i += PCI_VPD_SRDT_TAG_SIZE + pci_vpd_srdt_size(&buf[i]); } } return -1; } /* Search for matching key/subkey in the VPD data */ static int pci_vpd_find_info_subkey(const u8 *buf, unsigned int off, unsigned int len, const char *kw, const char *skw) { int i; for (i = off; i + PCI_VPD_INFO_FLD_HDR_SIZE <= off+len;) { /* Match key and subkey names, can use * for regex */ if ((kw[0] == '*' || buf[i+0] == kw[0]) && (kw[1] == '*' || buf[i+1] == kw[1]) && (skw[0] == '*' || !memcmp(&buf[i+3], skw, 3))) return i; i += PCI_VPD_INFO_FLD_HDR_SIZE + pci_vpd_info_field_size(&buf[i]); } return -1; } static int parse_vpd(struct libbiosdevname_state *state, struct pci_device *pdev, int len, unsigned char *vpd) { int i, j, k, isz, jsz, port, func, pfi; struct pci_device *vf; i = pci_vpd_find_tag(vpd, 0, len, 0x90); if (i < 0) return 1; isz = pci_vpd_lrdt_size(&vpd[i]); i += PCI_VPD_LRDT_TAG_SIZE; /* Lookup Version */ j = pci_vpd_find_info_subkey(vpd, i, isz, "**", "DSV"); if (j < 0) return 1; jsz = pci_vpd_info_field_size(&vpd[j]); j += PCI_VPD_INFO_FLD_HDR_SIZE; if (memcmp(vpd+j+3, "1028VPDR.VER1.0", 15)) return 1; /* Lookup Port Mappings */ j = pci_vpd_find_info_subkey(vpd, i, isz, "**", "DCM"); if (j < 0) return 1; jsz = pci_vpd_info_field_size(&vpd[j]); j += PCI_VPD_INFO_FLD_HDR_SIZE; for (k=3; kpci_dev->domain, pdev->pci_dev->bus, pdev->pci_dev->dev, func)) != NULL) { if (vf->vpd_port == INT_MAX) { vf->vpd_port = port; vf->vpd_pfi = pfi; } } } return 0; } /* Read and parse PCI VPD section if it exists */ static int read_pci_vpd(struct libbiosdevname_state *state, struct pci_device *pdev) { char path[PATH_MAX]; char pci_name[16]; int fd, rc=1; unsigned char *vpd; off_t size; ssize_t nrd; unparse_pci_name(pci_name, sizeof(pci_name), pdev->pci_dev); snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/vpd", pci_name); if ((fd = open(path, O_RDONLY|O_SYNC)) >= 0) { size = pci_vpd_size(pdev, fd); if (size > 0) { vpd = malloc(size); if (vpd != NULL) { if ((nrd = pread(fd, vpd, size, 0)) > 0) rc = parse_vpd(state, pdev, nrd, vpd); free(vpd); } } close(fd); } return rc; } static void set_pci_vpd_instance(struct libbiosdevname_state *state) { struct pci_device *dev, *dev2; /* Read VPD information for each device */ list_for_each_entry(dev, &state->pci_devices, node) { read_pci_vpd(state, dev); } /* Now match VPD master device */ list_for_each_entry(dev, &state->pci_devices, node) { if (dev->vpd_port == INT_MAX) continue; list_for_each_entry(dev2, &state->pci_devices, node) { if (dev2->pci_dev->domain == dev->pci_dev->domain && dev2->pci_dev->bus == dev->pci_dev->bus && dev2->pci_dev->dev == dev->pci_dev->dev && dev2->vpd_port == dev->vpd_port) { dev2->vpd_count++; dev->vpd_pf = dev2; break; } } } /* Delete all VPD devices with single function */ list_for_each_entry(dev, &state->pci_devices, node) { if (dev->vpd_count == 1) { dev->vpd_port = INT_MAX; dev->vpd_pfi = INT_MAX; dev->vpd_pf = NULL; } } } static int pci_find_capability(struct pci_dev *p, int cap) { u16 status; u8 hdr, id; int pos, ttl = 48; status = pci_read_word(p, PCI_STATUS); if (!(status & PCI_STATUS_CAP_LIST)) return 0; hdr = pci_read_byte(p, PCI_HEADER_TYPE); switch(hdr & 0x7F) { case PCI_HEADER_TYPE_NORMAL: case PCI_HEADER_TYPE_BRIDGE: pos = PCI_CAPABILITY_LIST; break; case PCI_HEADER_TYPE_CARDBUS: pos = PCI_CB_CAPABILITY_LIST; break; default: return 0; } while (ttl--) { pos = pci_read_byte(p, pos); if (pos < 0x40) break; pos &= ~3; id = pci_read_byte(p, pos+PCI_CAP_LIST_ID); if (id == 0xFF) break; if (id == cap) return pos; pos += PCI_CAP_LIST_NEXT; } return 0; } static struct pci_device * find_parent(struct libbiosdevname_state *state, struct pci_device *dev); #if 0 static int pcie_get_slot(struct libbiosdevname_state *state, struct pci_device *p) { int pos; u32 slot, flag; while (p) { /* Return PCIE physical slot number */ if ((pos = pci_find_capability(p->pci_dev, PCI_CAP_ID_EXP)) != 0) { flag = pci_read_word(p->pci_dev, pos + PCI_EXP_FLAGS); slot = (pci_read_long(p->pci_dev, pos + PCI_EXP_SLTCAP) >> 19); if ((flag & PCI_EXP_FLAGS_SLOT) && slot) return slot; } p = find_parent(state, p); } return PHYSICAL_SLOT_UNKNOWN; } #endif static int read_pci_sysfs_path(char *buf, size_t bufsize, const struct pci_dev *pdev) { char path[PATH_MAX]; char pci_name[16]; ssize_t size; unparse_pci_name(pci_name, sizeof(pci_name), pdev); snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s", pci_name); size = readlink(path, buf, bufsize); if (size == -1) return 1; return 0; } static int read_pci_sysfs_physfn(char *buf, size_t bufsize, const struct pci_dev *pdev) { char path[PATH_MAX]; char pci_name[16]; ssize_t size; unparse_pci_name(pci_name, sizeof(pci_name), pdev); snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/physfn", pci_name); size = readlink(path, buf, bufsize); if (size == -1) return 1; return 0; } static int virtfn_filter(const struct dirent *dent) { return (!strncmp(dent->d_name,"virtfn",6)); } static int _read_virtfn_index(unsigned int *index, const char *path, const char *basename, const char *pci_name) { char buf[PATH_MAX], *b; char fullpath[PATH_MAX]; ssize_t size; unsigned int u=INT_MAX; int scanned, rc=1; snprintf(fullpath, sizeof(fullpath), "%s/%s", path, basename); size = readlink(fullpath, buf, sizeof(buf)); if (size > 0) { /* form is ../0000:05:10.0 */ b=buf+3; /* skip ../ */ if (strlen(b) == strlen(pci_name) && !strncmp(b, pci_name, strlen(pci_name))) { scanned = sscanf(basename, "virtfn%u", &u); if (scanned == 1) { rc = 0; *index = u; } } } return rc; } static int read_virtfn_index(unsigned int *index, const struct pci_dev *pdev) { char pci_name[16]; char path[PATH_MAX]; char cpath[PATH_MAX]; struct dirent **namelist; int n, rc=1; unparse_pci_name(pci_name, sizeof(pci_name), pdev); snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/physfn", pci_name); if (realpath(path, cpath) == NULL) return rc; n = scandir(cpath, &namelist, virtfn_filter, versionsort); if (n < 0) return rc; else { while (n--) { if (rc) rc = _read_virtfn_index(index, cpath, namelist[n]->d_name, pci_name); free(namelist[n]); } free(namelist); } return rc; } int parse_pci_name(const char *s, int *domain, int *bus, int *dev, int *func) { int err; /* The domain part was added in 2.6 kernels. Test for that first. */ err = sscanf(s, "%x:%2x:%2x.%x", domain, bus, dev, func); if (err != 4) { err = sscanf(s, "%2x:%2x.%x", bus, dev, func); if (err != 3) { return 1; } } return 0; } static struct pci_dev * find_pdev_by_pci_name(struct pci_access *pacc, const char *s) { int domain=0, bus=0, device=0, func=0; if (parse_pci_name(s, &domain, &bus, &device, &func)) return NULL; return pci_get_dev(pacc, domain, bus, device, func); } static struct pci_device * find_physfn(struct libbiosdevname_state *state, struct pci_device *dev) { int rc; char path[PATH_MAX]; char *c; struct pci_dev *pdev; memset(path, 0, sizeof(path)); rc = read_pci_sysfs_physfn(path, sizeof(path), dev->pci_dev); if (rc != 0) return NULL; /* we get back a string like ../0000:05:0.0 where the last component is the parent device */ /* find the last backslash */ c = rindex(path, '/'); c++; pdev = find_pdev_by_pci_name(state->pacc, c); dev = find_dev_by_pci(state, pdev); return dev; } static int is_same_pci(const struct pci_dev *a, const struct pci_dev *b) { if (pci_domain_nr(a) == pci_domain_nr(b) && a->bus == b->bus && a->dev == b->dev && a->func == b->func) return 1; return 0; } static void try_add_vf_to_pf(struct libbiosdevname_state *state, struct pci_device *vf) { struct pci_device *pf; unsigned int index=0; int rc; pf = find_physfn(state, vf); if (!pf) return; list_add_tail(&vf->vfnode, &pf->vfs); rc = read_virtfn_index(&index, vf->pci_dev); if (!rc) { vf->vf_index = index; pf->is_sriov_physical_function = 1; } vf->pf = pf; vf->physical_slot = pf->physical_slot; } static struct pci_device * find_parent(struct libbiosdevname_state *state, struct pci_device *dev) { int rc; char path[PATH_MAX]; char *c; struct pci_device *physfn; struct pci_dev *pdev; memset(path, 0, sizeof(path)); /* if this device has a physfn pointer, then treat _that_ as the parent */ physfn = find_physfn(state, dev); if (physfn) { dev->is_sriov_virtual_function=1; return physfn; } rc = read_pci_sysfs_path(path, sizeof(path), dev->pci_dev); if (rc != 0) return NULL; /* we get back a string like ../../../devices/pci0000:00/0000:00:09.0/0000:05:17.4 where the last component is the device we asked for */ /* find the last backslash */ c = rindex(path, '/'); *c = '\0'; /* find the last backslash again */ c = rindex(path, '/'); c++; pdev = find_pdev_by_pci_name(state->pacc, c); if (pdev) { dev = find_dev_by_pci(state, pdev); return dev; } return NULL; } /* * Check our parents in case the device itself isn't listed * in the SMBIOS table. This has a problem, as * our parent bridge on a card may not be included * in the SMBIOS table. In that case, it falls back to "unknown". */ static inline int pci_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev) { return dev->physical_slot; } static inline int pirq_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev) { return pirq_pci_dev_to_slot(state->pirq_table, pci_domain_nr(dev->pci_dev), dev->pci_dev->bus, dev->pci_dev->dev); } static void dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev) { struct pci_device *d = dev; int slot; do { slot = pci_dev_to_slot(state, d); #if 0 /* PCI-E slot detection is broken?? */ if (slot == PHYSICAL_SLOT_UNKNOWN) slot = pcie_get_slot(state, d); #endif if (slot == PHYSICAL_SLOT_UNKNOWN) slot = pirq_dev_to_slot(state, d); if (slot == PHYSICAL_SLOT_UNKNOWN) d = find_parent(state, d); } while (d && slot == PHYSICAL_SLOT_UNKNOWN); dev->physical_slot = slot; } static char *read_pci_sysfs_label(const struct pci_dev *pdev) { char path[PATH_MAX]; char pci_name[16]; int rc; char *label = NULL; unparse_pci_name(pci_name, sizeof(pci_name), pdev); snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/label", pci_name); rc = sysfs_read_file(path, &label); if (rc == 0) return label; return NULL; } static int read_pci_sysfs_index(unsigned int *index, const struct pci_dev *pdev) { char path[PATH_MAX]; char pci_name[16]; int rc; char *indexstr = NULL; unsigned int i; unparse_pci_name(pci_name, sizeof(pci_name), pdev); snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/index", pci_name); rc = sysfs_read_file(path, &indexstr); if (rc == 0) { rc = sscanf(indexstr, "%u", &i); if (rc == 1) { *index = i; return 0; } } return 1; } static void fill_pci_dev_sysfs(struct pci_device *dev, struct pci_dev *p) { int rc; unsigned int index = 0; char *label = NULL; char buf[PATH_MAX]; unparse_pci_name(buf, sizeof(buf), p); rc = read_pci_sysfs_index(&index, p); if (!rc) { dev->sysfs_index = index; dev->uses_sysfs |= HAS_SYSFS_INDEX; } label = read_pci_sysfs_label(p); if (label) { dev->sysfs_label = label; dev->uses_sysfs |= HAS_SYSFS_LABEL; } } static void add_pci_dev(struct libbiosdevname_state *state, struct pci_dev *p) { struct pci_device *dev; dev = malloc(sizeof(*dev)); if (!dev) { fprintf(stderr, "out of memory\n"); return; } memset(dev, 0, sizeof(*dev)); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->vfnode); INIT_LIST_HEAD(&dev->vfs); dev->pci_dev = p; dev->physical_slot = PHYSICAL_SLOT_UNKNOWN; dev->class = pci_read_word(p, PCI_CLASS_DEVICE); dev->vf_index = INT_MAX; dev->vpd_port = INT_MAX; dev->vpd_pfi = INT_MAX; dev->vpd_pf = NULL; fill_pci_dev_sysfs(dev, p); list_add(&dev->node, &state->pci_devices); } void free_pci_devices(struct libbiosdevname_state *state) { struct pci_device *pos, *next; list_for_each_entry_safe(pos, next, &state->pci_devices, node) { if (pos->smbios_label) free(pos->smbios_label); if (pos->sysfs_label) free(pos->sysfs_label); list_del(&pos->node); free(pos); } } static void set_pci_slots(struct libbiosdevname_state *state) { struct pci_device *dev; list_for_each_entry(dev, &state->pci_devices, node) { dev_to_slot(state, dev); } } static int set_pci_slot_index(struct libbiosdevname_state *state) { struct pci_device *pcidev; int prevslot=-1; int index=1; /* only iterate over the PCI devices, because the bios_device list may be incomplete due to renames happening in parallel */ list_for_each_entry(pcidev, &state->pci_devices, node) { if (pcidev->physical_slot == 0) /* skip embedded devices */ continue; if (!is_pci_network(pcidev)) /* only look at PCI network devices */ continue; if (pcidev->is_sriov_virtual_function) /* skip sriov VFs, they're handled later */ continue; if (pcidev->physical_slot != prevslot) { index=1; prevslot = pcidev->physical_slot; } else index++; pcidev->index_in_slot = index; } return 0; } static int set_embedded_index(struct libbiosdevname_state *state) { struct pci_device *pcidev; int index=1; list_for_each_entry(pcidev, &state->pci_devices, node) { if (pcidev->physical_slot != 0) /* skip non-embedded devices */ continue; if (!is_pci_network(pcidev)) /* only look at PCI network devices */ continue; if (pcidev->is_sriov_virtual_function) /* skip sriov VFs, they're handled later */ continue; if (pcidev->vpd_port != INT_MAX) continue; pcidev->embedded_index = index; pcidev->embedded_index_valid = 1; index++; } return 0; } static void set_sriov_pf_vf(struct libbiosdevname_state *state) { struct pci_device *vf; list_for_each_entry(vf, &state->pci_devices, node) { if (!vf->is_sriov_virtual_function) continue; try_add_vf_to_pf(state, vf); } } /* * This sorts the PCI devices by breadth-first domain/bus/dev/fn. */ static int sort_pci(const struct pci_device *a, const struct pci_device *b) { if (pci_domain_nr(a->pci_dev) < pci_domain_nr(b->pci_dev)) return -1; else if (pci_domain_nr(a->pci_dev) > pci_domain_nr(b->pci_dev)) return 1; if (a->pci_dev->bus < b->pci_dev->bus) return -1; else if (a->pci_dev->bus > b->pci_dev->bus) return 1; if (a->pci_dev->dev < b->pci_dev->dev) return -1; else if (a->pci_dev->dev > b->pci_dev->dev) return 1; if (a->pci_dev->func < b->pci_dev->func) return -1; else if (a->pci_dev->func > b->pci_dev->func) return 1; return 0; } static void insertion_sort_devices(struct pci_device *a, struct list_head *list, int (*cmp)(const struct pci_device *, const struct pci_device *)) { struct pci_device *b; list_for_each_entry(b, list, node) { if (cmp(a, b) <= 0) { list_move_tail(&a->node, &b->node); return; } } list_move_tail(&a->node, list); } static void sort_device_list(struct libbiosdevname_state *state) { LIST_HEAD(sorted_devices); struct pci_device *dev, *tmp; list_for_each_entry_safe(dev, tmp, &state->pci_devices, node) { insertion_sort_devices(dev, &sorted_devices, sort_pci); } list_splice(&sorted_devices, &state->pci_devices); } int get_pci_devices(struct libbiosdevname_state *state) { struct pci_access *pacc; struct pci_dev *p; struct routing_table *table; int rc=0; table = pirq_alloc_read_table(); if (table) state->pirq_table = table; pacc = pci_alloc(); if (!pacc) return rc; state->pacc = pacc; pci_init(pacc); pci_scan_bus(pacc); for (p=pacc->devices; p; p=p->next) { add_pci_dev(state, p); } /* ordering here is important */ dmidecode_main(state); /* this will fail on Xen guests, that's OK */ sort_device_list(state); set_pci_vpd_instance(state); set_pci_slots(state); set_embedded_index(state); set_pci_slot_index(state); set_sriov_pf_vf(state); return rc; } int unparse_pci_name(char *buf, int size, const struct pci_dev *pdev) { return snprintf(buf, size, "%04x:%02x:%02x.%x", pci_domain_nr(pdev), pdev->bus, pdev->dev, pdev->func); } static int unparse_location(char *buf, const int size, const int location) { char *s = buf; if (location == 0) s += snprintf(s, size-(s-buf), "embedded"); else if (location == INT_MAX) s += snprintf(s, size-(s-buf), "unknown"); else if (location > 0) s += snprintf(s, size-(s-buf), "%u", location); else s += snprintf(s, size-(s-buf), "unknown"); return (s-buf); } static int unparse_smbios_type41_type(char *buf, const int size, const int type) { char *s = buf; const char *msg[] = {"Other", "Unknown", "Video", "SCSI Controller", "Ethernet", "Token Ring", "Sound", "PATA Controller", "SATA Controller", "SAS Controller", }; if (type > 0 && type <= sizeof(msg)) s += snprintf(s, size-(s-buf), "%s\n", msg[type-1]); else s += snprintf(s, size-(s-buf), "\n"); return (s-buf); } int unparse_pci_device(char *buf, const int size, const struct pci_device *p) { char *s = buf; struct pci_device *dev; char pci_name[16]; s += snprintf(s, size-(s-buf), "PCI name : "); s += unparse_pci_name(s, size-(s-buf), p->pci_dev); s += snprintf(s, size-(s-buf), "\n"); s += snprintf(s, size-(s-buf), "PCI Slot : "); if (p->physical_slot < INT_MAX) s += unparse_location(s, size-(s-buf), p->physical_slot); else s += snprintf(s, size-(s-buf), "Unknown"); s += snprintf(s, size-(s-buf), "\n"); if (p->smbios_type) { s += snprintf(s, size-(s-buf), "SMBIOS Device Type: "); s += unparse_smbios_type41_type(s, size-(s-buf), p->smbios_type); if (p->smbios_instance) s += snprintf(s, size-(s-buf), "SMBIOS Instance: %u\n", p->smbios_instance); } if (p->uses_smbios & HAS_SMBIOS_LABEL && p->smbios_label) s += snprintf(s, size-(s-buf), "SMBIOS Label: %s\n", p->smbios_label); if (p->uses_sysfs & HAS_SYSFS_INDEX) s += snprintf(s, size-(s-buf), "sysfs Index: %u\n", p->sysfs_index); if (p->uses_sysfs & HAS_SYSFS_LABEL) s += snprintf(s, size-(s-buf), "sysfs Label: %s\n", p->sysfs_label); if (p->physical_slot > 0 && !p->is_sriov_virtual_function) s += snprintf(s, size-(s-buf), "Index in slot: %u\n", p->index_in_slot); if (p->embedded_index_valid) s += snprintf(s, size-(s-buf), "Embedded Index: %u\n", p->embedded_index); if (p->vpd_port < INT_MAX) { s += snprintf(s, size-(s-buf), "VPD Port: %u\n", p->vpd_port); s += snprintf(s, size-(s-buf), "VPD Index: %u\n", p->vpd_pfi); if (p->vpd_pf) { s += snprintf(s, size-(s-buf), "VPD PCI master: "); s += unparse_pci_name(s, size-(s-buf), p->vpd_pf->pci_dev); s += snprintf(s, size-(s-buf), " count %d\n", p->vpd_pf->vpd_count); } } if (!list_empty(&p->vfs)) { s += snprintf(s, size-(s-buf), "Virtual Functions:\n"); list_for_each_entry(dev, &p->vfs, vfnode) { unparse_pci_name(pci_name, sizeof(pci_name), dev->pci_dev); s += snprintf(s, size-(s-buf), "%s\n", pci_name); } } return (s-buf); } struct pci_device * find_dev_by_pci(const struct libbiosdevname_state *state, const struct pci_dev *p) { struct pci_device *dev; list_for_each_entry(dev, &state->pci_devices, node) { if (is_same_pci(p, dev->pci_dev)) return dev; } return NULL; } struct pci_device * find_pci_dev_by_pci_addr(const struct libbiosdevname_state *state, const int domain, const int bus, const int device, const int func) { struct pci_device *dev; struct pci_dev p; memset(&p, 0, sizeof(p)); #ifdef HAVE_STRUCT_PCI_DEV_DOMAIN p.domain = domain; #endif p.bus = bus; p.dev = device; p.func = func; list_for_each_entry(dev, &state->pci_devices, node) { if (is_same_pci(&p, dev->pci_dev)) return dev; } return NULL; } struct pci_device * find_dev_by_pci_name(const struct libbiosdevname_state *state, const char *s) { int domain=0, bus=0, device=0, func=0; if (parse_pci_name(s, &domain, &bus, &device, &func)) return NULL; return find_pci_dev_by_pci_addr(state, domain, bus, device, func); }