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