]>
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 | { | |
79 | word class = d->dev->device_class; | |
80 | byte ht = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f; | |
81 | if (class == PCI_CLASS_BRIDGE_PCI && | |
82 | (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS)) | |
83 | { | |
84 | b = xmalloc(sizeof(struct bridge)); | |
85 | b->domain = d->dev->domain; | |
86 | if (ht == PCI_HEADER_TYPE_BRIDGE) | |
87 | { | |
88 | b->primary = get_conf_byte(d, PCI_PRIMARY_BUS); | |
89 | b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS); | |
90 | b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS); | |
91 | } | |
92 | else | |
93 | { | |
94 | b->primary = get_conf_byte(d, PCI_CB_PRIMARY_BUS); | |
95 | b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS); | |
96 | b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS); | |
97 | } | |
98 | *last_br = b; | |
99 | last_br = &b->chain; | |
100 | b->next = b->child = NULL; | |
101 | b->first_bus = NULL; | |
102 | b->br_dev = d; | |
6b056c8e | 103 | d->bridge = b; |
c7a34993 MM |
104 | } |
105 | } | |
106 | *last_br = NULL; | |
107 | ||
108 | /* Create a bridge tree */ | |
109 | ||
110 | for (b=&host_bridge; b; b=b->chain) | |
111 | { | |
112 | struct bridge *c, *best; | |
113 | best = NULL; | |
114 | for (c=&host_bridge; c; c=c->chain) | |
115 | if (c != b && (c == &host_bridge || b->domain == c->domain) && | |
116 | b->primary >= c->secondary && b->primary <= c->subordinate && | |
117 | (!best || best->subordinate - best->primary > c->subordinate - c->primary)) | |
118 | best = c; | |
119 | if (best) | |
120 | { | |
121 | b->next = best->child; | |
122 | best->child = b; | |
123 | } | |
124 | } | |
125 | ||
126 | /* Insert secondary bus for each bridge */ | |
127 | ||
128 | for (b=&host_bridge; b; b=b->chain) | |
129 | if (!find_bus(b, b->domain, b->secondary)) | |
130 | new_bus(b, b->domain, b->secondary); | |
131 | ||
132 | /* Create bus structs and link devices */ | |
133 | ||
6b056c8e MM |
134 | for (d=first_dev; d; d=d->next) |
135 | insert_dev(d, &host_bridge); | |
c7a34993 MM |
136 | } |
137 | ||
138 | static void | |
139 | print_it(char *line, char *p) | |
140 | { | |
141 | *p++ = '\n'; | |
142 | *p = 0; | |
143 | fputs(line, stdout); | |
144 | for (p=line; *p; p++) | |
145 | if (*p == '+' || *p == '|') | |
146 | *p = '|'; | |
147 | else | |
148 | *p = ' '; | |
149 | } | |
150 | ||
151 | static void show_tree_bridge(struct bridge *, char *, char *); | |
152 | ||
153 | static void | |
154 | show_tree_dev(struct device *d, char *line, char *p) | |
155 | { | |
156 | struct pci_dev *q = d->dev; | |
157 | struct bridge *b; | |
158 | char namebuf[256]; | |
159 | ||
160 | p += sprintf(p, "%02x.%x", q->dev, q->func); | |
161 | for (b=&host_bridge; b; b=b->chain) | |
162 | if (b->br_dev == d) | |
163 | { | |
164 | if (b->secondary == b->subordinate) | |
886263b3 | 165 | p += sprintf(p, "-[%02x]-", b->secondary); |
c7a34993 | 166 | else |
886263b3 | 167 | p += sprintf(p, "-[%02x-%02x]-", b->secondary, b->subordinate); |
c7a34993 MM |
168 | show_tree_bridge(b, line, p); |
169 | return; | |
170 | } | |
171 | if (verbose) | |
172 | p += sprintf(p, " %s", | |
173 | pci_lookup_name(pacc, namebuf, sizeof(namebuf), | |
174 | PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE, | |
175 | q->vendor_id, q->device_id)); | |
176 | print_it(line, p); | |
177 | } | |
178 | ||
179 | static void | |
180 | show_tree_bus(struct bus *b, char *line, char *p) | |
181 | { | |
182 | if (!b->first_dev) | |
183 | print_it(line, p); | |
6b056c8e | 184 | else if (!b->first_dev->bus_next) |
c7a34993 MM |
185 | { |
186 | *p++ = '-'; | |
187 | *p++ = '-'; | |
188 | show_tree_dev(b->first_dev, line, p); | |
189 | } | |
190 | else | |
191 | { | |
192 | struct device *d = b->first_dev; | |
6b056c8e | 193 | while (d->bus_next) |
c7a34993 MM |
194 | { |
195 | p[0] = '+'; | |
196 | p[1] = '-'; | |
197 | show_tree_dev(d, line, p+2); | |
6b056c8e | 198 | d = d->bus_next; |
c7a34993 MM |
199 | } |
200 | p[0] = '\\'; | |
201 | p[1] = '-'; | |
202 | show_tree_dev(d, line, p+2); | |
203 | } | |
204 | } | |
205 | ||
206 | static void | |
207 | show_tree_bridge(struct bridge *b, char *line, char *p) | |
208 | { | |
209 | *p++ = '-'; | |
210 | if (!b->first_bus->sibling) | |
211 | { | |
212 | if (b == &host_bridge) | |
213 | p += sprintf(p, "[%04x:%02x]-", b->domain, b->first_bus->number); | |
214 | show_tree_bus(b->first_bus, line, p); | |
215 | } | |
216 | else | |
217 | { | |
218 | struct bus *u = b->first_bus; | |
219 | char *k; | |
220 | ||
221 | while (u->sibling) | |
222 | { | |
223 | k = p + sprintf(p, "+-[%04x:%02x]-", u->domain, u->number); | |
224 | show_tree_bus(u, line, k); | |
225 | u = u->sibling; | |
226 | } | |
227 | k = p + sprintf(p, "\\-[%04x:%02x]-", u->domain, u->number); | |
228 | show_tree_bus(u, line, k); | |
229 | } | |
230 | } | |
231 | ||
232 | void | |
233 | show_forest(void) | |
234 | { | |
235 | char line[256]; | |
236 | ||
237 | grow_tree(); | |
238 | show_tree_bridge(&host_bridge, line, line); | |
239 | } |