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