]>
Commit | Line | Data |
---|---|---|
c7a34993 MM |
1 | /* |
2 | * The PCI Utilities -- Show Bus Tree | |
3 | * | |
6b056c8e | 4 | * Copyright (c) 1997--2018 Martin Mares <mj@ucw.cz> |
c7a34993 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <string.h> | |
11 | ||
12 | #include "lspci.h" | |
13 | ||
6b056c8e | 14 | struct bridge host_bridge = { NULL, NULL, NULL, NULL, 0, ~0, 0, ~0, NULL }; |
c7a34993 MM |
15 | |
16 | static struct bus * | |
17 | find_bus(struct bridge *b, unsigned int domain, unsigned int n) | |
18 | { | |
19 | struct bus *bus; | |
20 | ||
21 | for (bus=b->first_bus; bus; bus=bus->sibling) | |
22 | if (bus->domain == domain && bus->number == n) | |
23 | break; | |
24 | return bus; | |
25 | } | |
26 | ||
27 | static struct bus * | |
28 | new_bus(struct bridge *b, unsigned int domain, unsigned int n) | |
29 | { | |
30 | struct bus *bus = xmalloc(sizeof(struct bus)); | |
31 | bus->domain = domain; | |
32 | bus->number = n; | |
33 | bus->sibling = b->first_bus; | |
34 | bus->first_dev = NULL; | |
35 | bus->last_dev = &bus->first_dev; | |
6b056c8e | 36 | bus->parent_bridge = b; |
c7a34993 MM |
37 | b->first_bus = bus; |
38 | return bus; | |
39 | } | |
40 | ||
41 | static void | |
42 | insert_dev(struct device *d, struct bridge *b) | |
43 | { | |
44 | struct pci_dev *p = d->dev; | |
45 | struct bus *bus; | |
46 | ||
47 | if (! (bus = find_bus(b, p->domain, p->bus))) | |
48 | { | |
49 | struct bridge *c; | |
50 | for (c=b->child; c; c=c->next) | |
ab61451d | 51 | if (c->domain == (unsigned)p->domain && c->secondary <= p->bus && p->bus <= c->subordinate) |
c7a34993 MM |
52 | { |
53 | insert_dev(d, c); | |
54 | return; | |
55 | } | |
56 | bus = new_bus(b, p->domain, p->bus); | |
57 | } | |
58 | /* Simple insertion at the end _does_ guarantee the correct order as the | |
59 | * original device list was sorted by (domain, bus, devfn) lexicographically | |
60 | * and all devices on the new list have the same bus number. | |
61 | */ | |
62 | *bus->last_dev = d; | |
6b056c8e MM |
63 | bus->last_dev = &d->bus_next; |
64 | d->bus_next = NULL; | |
65 | d->parent_bus = bus; | |
c7a34993 MM |
66 | } |
67 | ||
6b056c8e | 68 | void |
c7a34993 MM |
69 | grow_tree(void) |
70 | { | |
6b056c8e | 71 | struct device *d; |
c7a34993 MM |
72 | struct bridge **last_br, *b; |
73 | ||
74 | /* Build list of bridges */ | |
75 | ||
76 | last_br = &host_bridge.chain; | |
77 | for (d=first_dev; d; d=d->next) | |
78 | { | |
f558905d MM |
79 | struct pci_dev *dd = d->dev; |
80 | word class = dd->device_class; | |
c7a34993 | 81 | byte ht = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f; |
f558905d | 82 | if ((class >> 8) == PCI_BASE_CLASS_BRIDGE && |
c7a34993 MM |
83 | (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS)) |
84 | { | |
85 | b = xmalloc(sizeof(struct bridge)); | |
f558905d | 86 | b->domain = dd->domain; |
c7a34993 MM |
87 | if (ht == PCI_HEADER_TYPE_BRIDGE) |
88 | { | |
89 | b->primary = get_conf_byte(d, PCI_PRIMARY_BUS); | |
90 | b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS); | |
91 | b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS); | |
92 | } | |
93 | else | |
94 | { | |
95 | b->primary = get_conf_byte(d, PCI_CB_PRIMARY_BUS); | |
96 | b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS); | |
97 | b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS); | |
98 | } | |
99 | *last_br = b; | |
100 | last_br = &b->chain; | |
101 | b->next = b->child = NULL; | |
102 | b->first_bus = NULL; | |
103 | b->br_dev = d; | |
6b056c8e | 104 | d->bridge = b; |
f558905d MM |
105 | pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n", |
106 | dd->domain, dd->bus, dd->dev, dd->func, | |
107 | b->primary, b->secondary, b->subordinate); | |
c7a34993 MM |
108 | } |
109 | } | |
110 | *last_br = NULL; | |
111 | ||
112 | /* Create a bridge tree */ | |
113 | ||
114 | for (b=&host_bridge; b; b=b->chain) | |
115 | { | |
116 | struct bridge *c, *best; | |
117 | best = NULL; | |
118 | for (c=&host_bridge; c; c=c->chain) | |
119 | if (c != b && (c == &host_bridge || b->domain == c->domain) && | |
120 | b->primary >= c->secondary && b->primary <= c->subordinate && | |
121 | (!best || best->subordinate - best->primary > c->subordinate - c->primary)) | |
122 | best = c; | |
123 | if (best) | |
124 | { | |
125 | b->next = best->child; | |
126 | best->child = b; | |
127 | } | |
128 | } | |
129 | ||
130 | /* Insert secondary bus for each bridge */ | |
131 | ||
132 | for (b=&host_bridge; b; b=b->chain) | |
133 | if (!find_bus(b, b->domain, b->secondary)) | |
134 | new_bus(b, b->domain, b->secondary); | |
135 | ||
136 | /* Create bus structs and link devices */ | |
137 | ||
6b056c8e MM |
138 | for (d=first_dev; d; d=d->next) |
139 | insert_dev(d, &host_bridge); | |
c7a34993 MM |
140 | } |
141 | ||
142 | static void | |
143 | print_it(char *line, char *p) | |
144 | { | |
145 | *p++ = '\n'; | |
146 | *p = 0; | |
147 | fputs(line, stdout); | |
148 | for (p=line; *p; p++) | |
149 | if (*p == '+' || *p == '|') | |
150 | *p = '|'; | |
151 | else | |
152 | *p = ' '; | |
153 | } | |
154 | ||
155 | static void show_tree_bridge(struct bridge *, char *, char *); | |
156 | ||
157 | static void | |
158 | show_tree_dev(struct device *d, char *line, char *p) | |
159 | { | |
160 | struct pci_dev *q = d->dev; | |
161 | struct bridge *b; | |
162 | char namebuf[256]; | |
163 | ||
164 | p += sprintf(p, "%02x.%x", q->dev, q->func); | |
165 | for (b=&host_bridge; b; b=b->chain) | |
166 | if (b->br_dev == d) | |
167 | { | |
168 | if (b->secondary == b->subordinate) | |
886263b3 | 169 | p += sprintf(p, "-[%02x]-", b->secondary); |
c7a34993 | 170 | else |
886263b3 | 171 | p += sprintf(p, "-[%02x-%02x]-", b->secondary, b->subordinate); |
c7a34993 MM |
172 | show_tree_bridge(b, line, p); |
173 | return; | |
174 | } | |
175 | if (verbose) | |
176 | p += sprintf(p, " %s", | |
177 | pci_lookup_name(pacc, namebuf, sizeof(namebuf), | |
178 | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, | |
179 | q->vendor_id, q->device_id)); | |
180 | print_it(line, p); | |
181 | } | |
182 | ||
183 | static void | |
184 | show_tree_bus(struct bus *b, char *line, char *p) | |
185 | { | |
186 | if (!b->first_dev) | |
187 | print_it(line, p); | |
6b056c8e | 188 | else if (!b->first_dev->bus_next) |
c7a34993 MM |
189 | { |
190 | *p++ = '-'; | |
191 | *p++ = '-'; | |
192 | show_tree_dev(b->first_dev, line, p); | |
193 | } | |
194 | else | |
195 | { | |
196 | struct device *d = b->first_dev; | |
6b056c8e | 197 | while (d->bus_next) |
c7a34993 MM |
198 | { |
199 | p[0] = '+'; | |
200 | p[1] = '-'; | |
201 | show_tree_dev(d, line, p+2); | |
6b056c8e | 202 | d = d->bus_next; |
c7a34993 MM |
203 | } |
204 | p[0] = '\\'; | |
205 | p[1] = '-'; | |
206 | show_tree_dev(d, line, p+2); | |
207 | } | |
208 | } | |
209 | ||
210 | static void | |
211 | show_tree_bridge(struct bridge *b, char *line, char *p) | |
212 | { | |
213 | *p++ = '-'; | |
214 | if (!b->first_bus->sibling) | |
215 | { | |
216 | if (b == &host_bridge) | |
217 | p += sprintf(p, "[%04x:%02x]-", b->domain, b->first_bus->number); | |
218 | show_tree_bus(b->first_bus, line, p); | |
219 | } | |
220 | else | |
221 | { | |
222 | struct bus *u = b->first_bus; | |
223 | char *k; | |
224 | ||
225 | while (u->sibling) | |
226 | { | |
227 | k = p + sprintf(p, "+-[%04x:%02x]-", u->domain, u->number); | |
228 | show_tree_bus(u, line, k); | |
229 | u = u->sibling; | |
230 | } | |
231 | k = p + sprintf(p, "\\-[%04x:%02x]-", u->domain, u->number); | |
232 | show_tree_bus(u, line, k); | |
233 | } | |
234 | } | |
235 | ||
236 | void | |
237 | show_forest(void) | |
238 | { | |
239 | char line[256]; | |
240 | ||
241 | grow_tree(); | |
242 | show_tree_bridge(&host_bridge, line, line); | |
243 | } |