]> mj.ucw.cz Git - pciutils.git/blob - ls-tree.c
8e01dea18f7882bdfa0a243fe39b341dee18dba2
[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           b->primary = dd->bus;
117           if (ht == PCI_HEADER_TYPE_BRIDGE)
118             {
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->secondary = get_conf_byte(d, PCI_CB_CARD_BUS);
125               b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS);
126             }
127           *last_br = b;
128           last_br = &b->chain;
129           b->next = b->child = NULL;
130           b->first_bus = NULL;
131           b->last_bus = NULL;
132           b->br_dev = d;
133           d->bridge = b;
134           pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n",
135             dd->domain, dd->bus, dd->dev, dd->func,
136             b->primary, b->secondary, b->subordinate);
137         }
138     }
139
140   /* Append additional bridges reported by libpci via d->parent */
141
142   for (d=first_dev; d; d=d->next)
143     {
144       struct device *parent = NULL;
145       if (d->dev->known_fields & PCI_FILL_PARENT)
146         parent = find_device(d->dev->parent);
147       if (!parent || parent->bridge)
148         continue;
149       b = xmalloc(sizeof(struct bridge));
150       b->domain = parent->dev->domain;
151       b->primary = parent->dev->bus;
152       b->secondary = d->dev->bus;
153       /* At this stage subordinate number is unknown, so set it to secondary bus number. */
154       b->subordinate = b->secondary;
155       *last_br = b;
156       last_br = &b->chain;
157       b->next = b->child = NULL;
158       b->first_bus = NULL;
159       b->last_bus = NULL;
160       b->br_dev = parent;
161       parent->bridge = b;
162       pacc->debug("Tree: bridge %04x:%02x:%02x.%d\n", b->domain,
163         parent->dev->bus, parent->dev->dev, parent->dev->func);
164     }
165   *last_br = NULL;
166
167   /* Create a bridge tree */
168
169   for (b=&host_bridge; b; b=b->chain)
170     {
171       struct device *br_dev = b->br_dev;
172       struct bridge *c, *best = NULL;
173       struct device *parent = NULL;
174
175       if (br_dev && (br_dev->dev->known_fields & PCI_FILL_PARENT))
176         parent = find_device(br_dev->dev->parent);
177       if (parent)
178         best = parent->bridge;
179       if (!best)
180       for (c=&host_bridge; c; c=c->chain)
181         if (c != b && (c == &host_bridge || b->domain == c->domain) &&
182             b->primary >= c->secondary && b->primary <= c->subordinate &&
183             (!best || best->subordinate - best->primary > c->subordinate - c->primary))
184           best = c;
185       if (best)
186         {
187           b->next = best->child;
188           best->child = b;
189         }
190     }
191
192   /* Insert secondary bus for each bridge */
193
194   for (b=&host_bridge; b; b=b->chain)
195     if (!find_bus(b, b->domain, b->secondary))
196       new_bus(b, b->domain, b->secondary);
197
198   /* Create bus structs and link devices */
199
200   for (d=first_dev; d; d=d->next)
201     insert_dev(d, &host_bridge);
202 }
203
204 #define LINE_BUF_SIZE 1024
205
206 static void
207 print_it(char *line, char *p)
208 {
209   *p = 0;
210   fputs(line, stdout);
211   if (p >= line + LINE_BUF_SIZE - 1)
212     fputs("...", stdout);
213   putchar('\n');
214   for (p=line; *p; p++)
215     if (*p == '+' || *p == '|')
216       *p = '|';
217     else
218       *p = ' ';
219 }
220
221 static void show_tree_bridge(struct pci_filter *filter, struct bridge *, char *, char *);
222
223 static char * FORMAT_CHECK(printf, 3, 4)
224 tree_printf(char *line, char *p, char *fmt, ...)
225 {
226   va_list args;
227   int space = line + LINE_BUF_SIZE - 1 - p;
228
229   if (space <= 0)
230     return p;
231
232   va_start(args, fmt);
233   int res = vsnprintf(p, space, fmt, args);
234   if (res < 0)
235     {
236       /* Ancient C libraries return -1 on overflow and they do not truncate the output properly. */
237       *p = 0;
238       p += space;
239     }
240   else if (res >= space)
241     {
242       /* Ancient C libraries do not truncate the output properly. */
243       *(p+space-1) = 0;
244       p += space;
245     }
246   else
247     p += res;
248
249   va_end(args);
250   return p;
251 }
252
253 static void
254 show_tree_dev(struct pci_filter *filter, struct device *d, char *line, char *p)
255 {
256   struct pci_dev *q = d->dev;
257   struct bridge *b;
258   char namebuf[256];
259
260   p = tree_printf(line, p, "%02x.%x", q->dev, q->func);
261   for (b=&host_bridge; b; b=b->chain)
262     if (b->br_dev == d)
263       {
264         if (b->secondary == 0)
265           p = tree_printf(line, p, "-");
266         else if (b->secondary == b->subordinate)
267           p = tree_printf(line, p, "-[%02x]-", b->secondary);
268         else
269           p = tree_printf(line, p, "-[%02x-%02x]-", b->secondary, b->subordinate);
270         show_tree_bridge(filter, b, line, p);
271         return;
272       }
273   if (verbose)
274     p = tree_printf(line, p, "  %s",
275                     pci_lookup_name(pacc, namebuf, sizeof(namebuf),
276                                     PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
277                                     q->vendor_id, q->device_id));
278   print_it(line, p);
279 }
280
281 static struct pci_filter *
282 get_filter_for_child(struct pci_filter *filter, struct device *d)
283 {
284   if (!filter)
285     return NULL;
286
287   if (pci_filter_match(filter, d->dev))
288     return NULL;
289
290   return filter;
291 }
292
293 static int
294 check_bus_filter(struct pci_filter *filter, struct bus *b);
295
296 static int
297 check_dev_filter(struct pci_filter *filter, struct device *d)
298 {
299   struct bridge *br;
300   struct bus *b;
301
302   if (!filter)
303     return 1;
304
305   if (pci_filter_match(filter, d->dev))
306     return 1;
307
308   for (br = &host_bridge; br; br = br->chain)
309     if (br->br_dev == d)
310       {
311         for (b = br->first_bus; b; b = b->sibling)
312           if (check_bus_filter(filter, b))
313             return 1;
314         break;
315       }
316
317   return 0;
318 }
319
320 static int
321 check_bus_filter(struct pci_filter *filter, struct bus *b)
322 {
323   struct device *d;
324
325   if (!filter)
326     return 1;
327
328   for (d = b->first_dev; d; d = d->bus_next)
329     if (check_dev_filter(filter, d))
330       return 1;
331
332   return 0;
333 }
334
335 static void
336 show_tree_bus(struct pci_filter *filter, struct bus *b, char *line, char *p)
337 {
338   if (!b->first_dev)
339     print_it(line, p);
340   else if (!b->first_dev->bus_next)
341     {
342       if (check_dev_filter(filter, b->first_dev))
343         {
344           p = tree_printf(line, p, "--");
345           show_tree_dev(get_filter_for_child(filter, b->first_dev), b->first_dev, line, p);
346         }
347       else
348         print_it(line, p);
349     }
350   else
351     {
352       int i, count = 0;
353       struct device *d = b->first_dev;
354
355       do
356         {
357           if (check_dev_filter(filter, d))
358             count++;
359           d = d->bus_next;
360         }
361       while (d);
362
363       for (i = 0, d = b->first_dev; d; d = d->bus_next)
364         {
365           if (!check_dev_filter(filter, d))
366             continue;
367           char *p2 = tree_printf(line, p, count == 1 ? "--" : count == i+1 ? "\\-" : "+-");
368           show_tree_dev(get_filter_for_child(filter, d), d, line, p2);
369           i++;
370         }
371
372       if (count == 0)
373         print_it(line, p);
374     }
375 }
376
377 static void
378 show_tree_bridge(struct pci_filter *filter, struct bridge *b, char *line, char *p)
379 {
380   *p++ = '-';
381   if (!b->first_bus->sibling)
382     {
383       if (check_bus_filter(filter, b->first_bus))
384         {
385           if (b == &host_bridge)
386             p = tree_printf(line, p, "[%04x:%02x]-", b->domain, b->first_bus->number);
387           show_tree_bus(filter, b->first_bus, line, p);
388         }
389       else
390         print_it(line, p);
391     }
392   else
393     {
394       int i, count = 0;
395       struct bus *u = b->first_bus;
396       char *k;
397
398       do
399         {
400           if (check_bus_filter(filter, u))
401             count++;
402           u = u->sibling;
403         }
404       while (u);
405
406       for (i = 0, u = b->first_bus; u; u = u->sibling)
407         {
408           if (!check_bus_filter(filter, u))
409             continue;
410           k = tree_printf(line, p, count == 1 ? "[%04x:%02x]-" : count == i+1 ? "\\-[%04x:%02x]-" : "+-[%04x:%02x]-", u->domain, u->number);
411           show_tree_bus(filter, u, line, k);
412           i++;
413         }
414
415       if (count == 0)
416         print_it(line, p);
417     }
418 }
419
420 void
421 show_forest(struct pci_filter *filter)
422 {
423   char line[LINE_BUF_SIZE];
424   show_tree_bridge(filter, &host_bridge, line, line);
425 }