1 /* 2 * The PCI Library -- Capabilities 3 * 4 * Copyright (c) 2008 Martin Mares <[email protected]> 5 * 6 * Can be freely distributed and used under the terms of the GNU GPL v2+. 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11 #include <string.h> 12 13 #include "internal.h" 14 15 static void 16 pci_add_cap(struct pci_dev *d, unsigned int addr, unsigned int id, unsigned int type) 17 { 18 struct pci_cap *cap = pci_malloc(d->access, sizeof(*cap)); 19 20 if (d->last_cap) 21 d->last_cap->next = cap; 22 else 23 d->first_cap = cap; 24 d->last_cap = cap; 25 cap->next = NULL; 26 cap->addr = addr; 27 cap->id = id; 28 cap->type = type; 29 d->access->debug("%04x:%02x:%02x.%d: Found capability %04x of type %d at %04x\n", 30 d->domain, d->bus, d->dev, d->func, id, type, addr); 31 } 32 33 static void 34 pci_scan_trad_caps(struct pci_dev *d) 35 { 36 word status = pci_read_word(d, PCI_STATUS); 37 byte been_there[256]; 38 int where; 39 40 if (!(status & PCI_STATUS_CAP_LIST)) 41 return; 42 43 memset(been_there, 0, 256); 44 where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3; 45 while (where) 46 { 47 byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID); 48 byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3; 49 if (been_there[where]++) 50 break; 51 if (id == 0xff) 52 break; 53 pci_add_cap(d, where, id, PCI_CAP_NORMAL); 54 where = next; 55 } 56 } 57 58 static void 59 pci_scan_ext_caps(struct pci_dev *d) 60 { 61 byte been_there[0x1000]; 62 int where = 0x100; 63 64 if (!pci_find_cap(d, PCI_CAP_ID_EXP, PCI_CAP_NORMAL)) 65 return; 66 67 memset(been_there, 0, 0x1000); 68 do 69 { 70 u32 header; 71 int id; 72 73 header = pci_read_long(d, where); 74 if (!header || header == 0xffffffff) 75 break; 76 id = header & 0xffff; 77 if (been_there[where]++) 78 break; 79 pci_add_cap(d, where, id, PCI_CAP_EXTENDED); 80 where = (header >> 20) & ~3; 81 } 82 while (where); 83 } 84 85 void 86 pci_scan_caps(struct pci_dev *d, unsigned int want_fields) 87 { 88 if (want_fields & PCI_FILL_EXT_CAPS) 89 want_fields |= PCI_FILL_CAPS; 90 91 if (want_fill(d, want_fields, PCI_FILL_CAPS)) 92 pci_scan_trad_caps(d); 93 if (want_fill(d, want_fields, PCI_FILL_EXT_CAPS)) 94 pci_scan_ext_caps(d); 95 } 96 97 void 98 pci_free_caps(struct pci_dev *d) 99 { 100 struct pci_cap *cap; 101 102 while (cap = d->first_cap) 103 { 104 d->first_cap = cap->next; 105 pci_mfree(cap); 106 } 107 } 108 109 struct pci_cap * 110 pci_find_cap(struct pci_dev *d, unsigned int id, unsigned int type) 111 { 112 return pci_find_cap_nr(d, id, type, NULL); 113 } 114 115 /** 116 * Finds a particular capability of a device 117 * 118 * To select one capability if there are more than one with the same id, you 119 * can provide a pointer to an unsigned int that contains the index which you 120 * want as cap_number. If you don't care and are fine with the first one you 121 * can supply NULL. The cap_number will be replaced by the actual number 122 * of capabilities with that id. 123 */ 124 struct pci_cap * 125 pci_find_cap_nr(struct pci_dev *d, unsigned int id, unsigned int type, 126 unsigned int *cap_number) 127 { 128 struct pci_cap *c; 129 struct pci_cap *found = NULL; 130 unsigned int target = (cap_number ? *cap_number : 0); 131 unsigned int index = 0; 132 133 pci_fill_info_v38(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS)); 134 135 for (c=d->first_cap; c; c=c->next) 136 { 137 if (c->type == type && c->id == id) 138 { 139 if (target == index) 140 found = c; 141 index++; 142 } 143 } 144 145 if (cap_number) 146 *cap_number = index; 147 return found; 148 } 149