]> mj.ucw.cz Git - pciutils.git/blob - ls-tree.c
Merge pull request #140 from pali/ls-tree
[pciutils.git] / ls-tree.c
1 /*
2  *      The PCI Utilities -- Show Bus Tree
3  *
4  *      Copyright (c) 1997--2021 Martin Mares <mj@ucw.cz>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL.
7  */
8
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #include "lspci.h"
14
15 struct bridge host_bridge = { NULL, NULL, NULL, NULL, NULL, 0, ~0, 0, ~0, NULL };
16
17 static struct bus *
18 find_bus(struct bridge *b, unsigned int domain, unsigned int n)
19 {
20   struct bus *bus;
21
22   for (bus=b->first_bus; bus; bus=bus->sibling)
23     if (bus->domain == domain && bus->number == n)
24       break;
25   return bus;
26 }
27
28 static struct device *
29 find_device(struct pci_dev *dd)
30 {
31   struct device *d;
32
33   if (!dd)
34     return NULL;
35   for (d=first_dev; d; d=d->next)
36     if (d->dev == dd)
37       break;
38   return d;
39 }
40
41 static struct bus *
42 new_bus(struct bridge *b, unsigned int domain, unsigned int n)
43 {
44   struct bus *bus = xmalloc(sizeof(struct bus));
45   bus->domain = domain;
46   bus->number = n;
47   bus->sibling = NULL;
48   bus->first_dev = NULL;
49   bus->last_dev = &bus->first_dev;
50   bus->parent_bridge = b;
51   if (b->last_bus)
52     b->last_bus->sibling = bus;
53   b->last_bus = bus;
54   if (!b->first_bus)
55     b->first_bus = bus;
56   return bus;
57 }
58
59 static void
60 insert_dev(struct device *d, struct bridge *b)
61 {
62   struct pci_dev *p = d->dev;
63   struct device *parent = NULL;
64   struct bus *bus = NULL;
65
66   if (p->known_fields & PCI_FILL_PARENT)
67     parent = find_device(p->parent);
68
69   if (parent && parent->bridge)
70     {
71       bus = parent->bridge->first_bus;
72       if (!bus)
73         bus = new_bus(parent->bridge, p->domain, p->bus);
74     }
75
76   if (!bus && ! (bus = find_bus(b, p->domain, p->bus)))
77     {
78       struct bridge *c;
79       for (c=b->child; c; c=c->next)
80         if (c->domain == (unsigned)p->domain && c->secondary <= p->bus && p->bus <= c->subordinate)
81           {
82             insert_dev(d, c);
83             return;
84           }
85       bus = new_bus(b, p->domain, p->bus);
86     }
87   /* Simple insertion at the end _does_ guarantee the correct order as the
88    * original device list was sorted by (domain, bus, devfn) lexicographically
89    * and all devices on the new list have the same bus number.
90    */
91   *bus->last_dev = d;
92   bus->last_dev = &d->bus_next;
93   d->bus_next = NULL;
94   d->parent_bus = bus;
95 }
96
97 void
98 grow_tree(void)
99 {
100   struct device *d;
101   struct bridge **last_br, *b;
102
103   /* Build list of bridges */
104
105   last_br = &host_bridge.chain;
106   for (d=first_dev; d; d=d->next)
107     {
108       struct pci_dev *dd = d->dev;
109       word class = dd->device_class;
110       byte ht = d->no_config_access ? -1 : (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f);
111       if ((class >> 8) == PCI_BASE_CLASS_BRIDGE &&
112           (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS))
113         {
114           b = xmalloc(sizeof(struct bridge));
115           b->domain = dd->domain;
116           if (ht == PCI_HEADER_TYPE_BRIDGE)
117             {
118               b->primary = get_conf_byte(d, PCI_PRIMARY_BUS);
119               b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS);
120               b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS);
121             }
122           else
123             {
124               b->primary = get_conf_byte(d, PCI_CB_PRIMARY_BUS);
125               b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS);
126               b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS);
127             }
128           *last_br = b;
129           last_br = &b->chain;
130           b->next = b->child = NULL;
131           b->first_bus = NULL;
132           b->last_bus = NULL;
133           b->br_dev = d;
134           d->bridge = b;
135           pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n",
136             dd->domain, dd->bus, dd->dev, dd->func,
137             b->primary, b->secondary, b->subordinate);
138         }
139     }
140
141   /* Append additional bridges reported by libpci via d->parent */
142
143   for (d=first_dev; d; d=d->next)
144     {
145       struct device *parent = NULL;
146       if (d->dev->known_fields & PCI_FILL_PARENT)
147         parent = find_device(d->dev->parent);
148       if (!parent || parent->bridge)
149         continue;
150       b = xmalloc(sizeof(struct bridge));
151       b->domain = parent->dev->domain;
152       b->primary = parent->dev->bus;
153       b->secondary = d->dev->bus;
154       /* At this stage subordinate number is unknown, so set it to secondary bus number. */
155       b->subordinate = b->secondary;
156       *last_br = b;
157       last_br = &b->chain;
158       b->next = b->child = NULL;
159       b->first_bus = NULL;
160       b->last_bus = NULL;
161       b->br_dev = parent;
162       parent->bridge = b;
163       pacc->debug("Tree: bridge %04x:%02x:%02x.%d\n", b->domain,
164         parent->dev->bus, parent->dev->dev, parent->dev->func);
165     }
166   *last_br = NULL;
167
168   /* Create a bridge tree */
169
170   for (b=&host_bridge; b; b=b->chain)
171     {
172       struct device *br_dev = b->br_dev;
173       struct bridge *c, *best = NULL;
174       struct device *parent = NULL;
175
176       if (br_dev && (br_dev->dev->known_fields & PCI_FILL_PARENT))
177         parent = find_device(br_dev->dev->parent);
178       if (parent)
179         best = parent->bridge;
180       if (!best)
181       for (c=&host_bridge; c; c=c->chain)
182         if (c != b && (c == &host_bridge || b->domain == c->domain) &&
183             b->primary >= c->secondary && b->primary <= c->subordinate &&
184             (!best || best->subordinate - best->primary > c->subordinate - c->primary))
185           best = c;
186       if (best)
187         {
188           b->next = best->child;
189           best->child = b;
190         }
191     }
192
193   /* Insert secondary bus for each bridge */
194
195   for (b=&host_bridge; b; b=b->chain)
196     if (!find_bus(b, b->domain, b->secondary))
197       new_bus(b, b->domain, b->secondary);
198
199   /* Create bus structs and link devices */
200
201   for (d=first_dev; d; d=d->next)
202     insert_dev(d, &host_bridge);
203 }
204
205 #define LINE_BUF_SIZE 1024
206
207 static void
208 print_it(char *line, char *p)
209 {
210   *p = 0;
211   fputs(line, stdout);
212   if (p >= line + LINE_BUF_SIZE - 1)
213     fputs("...", stdout);
214   putchar('\n');
215   for (p=line; *p; p++)
216     if (*p == '+' || *p == '|')
217       *p = '|';
218     else
219       *p = ' ';
220 }
221
222 static void show_tree_bridge(struct pci_filter *filter, struct bridge *, char *, char *);
223
224 static char * FORMAT_CHECK(printf, 3, 4)
225 tree_printf(char *line, char *p, char *fmt, ...)
226 {
227   va_list args;
228   int space = line + LINE_BUF_SIZE - 1 - p;
229
230   if (space <= 0)
231     return p;
232
233   va_start(args, fmt);
234   int res = vsnprintf(p, space, fmt, args);
235   if (res < 0)
236     {
237       /* Ancient C libraries return -1 on overflow and they do not truncate the output properly. */
238       *p = 0;
239       p += space;
240     }
241   else if (res >= space)
242     {
243       /* Ancient C libraries do not truncate the output properly. */
244       *(p+space-1) = 0;
245       p += space;
246     }
247   else
248     p += res;
249
250   va_end(args);
251   return p;
252 }
253
254 static void
255 show_tree_dev(struct pci_filter *filter, struct device *d, char *line, char *p)
256 {
257   struct pci_dev *q = d->dev;
258   struct bridge *b;
259   char namebuf[256];
260
261   p = tree_printf(line, p, "%02x.%x", q->dev, q->func);
262   for (b=&host_bridge; b; b=b->chain)
263     if (b->br_dev == d)
264       {
265         if (b->secondary == 0)
266           p = tree_printf(line, p, "-");
267         else if (b->secondary == b->subordinate)
268           p = tree_printf(line, p, "-[%02x]-", b->secondary);
269         else
270           p = tree_printf(line, p, "-[%02x-%02x]-", b->secondary, b->subordinate);
271         show_tree_bridge(filter, b, line, p);
272         return;
273       }
274   if (verbose)
275     p = tree_printf(line, p, "  %s",
276                     pci_lookup_name(pacc, namebuf, sizeof(namebuf),
277                                     PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
278                                     q->vendor_id, q->device_id));
279   print_it(line, p);
280 }
281
282 static struct pci_filter *
283 get_filter_for_child(struct pci_filter *filter, struct device *d)
284 {
285   if (!filter)
286     return NULL;
287
288   if (pci_filter_match(filter, d->dev))
289     return NULL;
290
291   return filter;
292 }
293
294 static int
295 check_bus_filter(struct pci_filter *filter, struct bus *b);
296
297 static int
298 check_dev_filter(struct pci_filter *filter, struct device *d)
299 {
300   struct bridge *br;
301   struct bus *b;
302
303   if (!filter)
304     return 1;
305
306   if (pci_filter_match(filter, d->dev))
307     return 1;
308
309   for (br = &host_bridge; br; br = br->chain)
310     if (br->br_dev == d)
311       {
312         for (b = br->first_bus; b; b = b->sibling)
313           if (check_bus_filter(filter, b))
314             return 1;
315         break;
316       }
317
318   return 0;
319 }
320
321 static int
322 check_bus_filter(struct pci_filter *filter, struct bus *b)
323 {
324   struct device *d;
325
326   if (!filter)
327     return 1;
328
329   for (d = b->first_dev; d; d = d->bus_next)
330     if (check_dev_filter(filter, d))
331       return 1;
332
333   return 0;
334 }
335
336 static void
337 show_tree_bus(struct pci_filter *filter, struct bus *b, char *line, char *p)
338 {
339   if (!b->first_dev)
340     print_it(line, p);
341   else if (!b->first_dev->bus_next)
342     {
343       if (check_dev_filter(filter, b->first_dev))
344         {
345           p = tree_printf(line, p, "--");
346           show_tree_dev(get_filter_for_child(filter, b->first_dev), b->first_dev, line, p);
347         }
348       else
349         print_it(line, p);
350     }
351   else
352     {
353       int i, count = 0;
354       struct device *d = b->first_dev;
355
356       do
357         {
358           if (check_dev_filter(filter, d))
359             count++;
360           d = d->bus_next;
361         }
362       while (d);
363
364       for (i = 0, d = b->first_dev; d; d = d->bus_next)
365         {
366           if (!check_dev_filter(filter, d))
367             continue;
368           char *p2 = tree_printf(line, p, count == 1 ? "--" : count == i+1 ? "\\-" : "+-");
369           show_tree_dev(get_filter_for_child(filter, d), d, line, p2);
370           i++;
371         }
372
373       if (count == 0)
374         print_it(line, p);
375     }
376 }
377
378 static void
379 show_tree_bridge(struct pci_filter *filter, struct bridge *b, char *line, char *p)
380 {
381   *p++ = '-';
382   if (!b->first_bus->sibling)
383     {
384       if (check_bus_filter(filter, b->first_bus))
385         {
386           if (b == &host_bridge)
387             p = tree_printf(line, p, "[%04x:%02x]-", b->domain, b->first_bus->number);
388           show_tree_bus(filter, b->first_bus, line, p);
389         }
390       else
391         print_it(line, p);
392     }
393   else
394     {
395       int i, count = 0;
396       struct bus *u = b->first_bus;
397       char *k;
398
399       do
400         {
401           if (check_bus_filter(filter, u))
402             count++;
403           u = u->sibling;
404         }
405       while (u);
406
407       for (i = 0, u = b->first_bus; u; u = u->sibling)
408         {
409           if (!check_bus_filter(filter, u))
410             continue;
411           k = tree_printf(line, p, count == 1 ? "[%04x:%02x]-" : count == i+1 ? "\\-[%04x:%02x]-" : "+-[%04x:%02x]-", u->domain, u->number);
412           show_tree_bus(filter, u, line, k);
413           i++;
414         }
415
416       if (count == 0)
417         print_it(line, p);
418     }
419 }
420
421 void
422 show_forest(struct pci_filter *filter)
423 {
424   char line[LINE_BUF_SIZE];
425   show_tree_bridge(filter, &host_bridge, line, line);
426 }