]> mj.ucw.cz Git - pciutils.git/blob - ls-tree.c
Merge remote-tracking branch 'pali/windows'
[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 int
283 check_bus_filter(struct pci_filter *filter, struct bus *b);
284
285 static int
286 check_dev_filter(struct pci_filter *filter, struct device *d)
287 {
288   struct bridge *br;
289   struct bus *b;
290
291   if (!filter)
292     return 1;
293
294   if (pci_filter_match(filter, d->dev))
295     return 1;
296
297   for (br = &host_bridge; br; br = br->chain)
298     if (br->br_dev == d)
299       {
300         for (b = br->first_bus; b; b = b->sibling)
301           if (check_bus_filter(filter, b))
302             return 1;
303         break;
304       }
305
306   return 0;
307 }
308
309 static int
310 check_bus_filter(struct pci_filter *filter, struct bus *b)
311 {
312   struct device *d;
313
314   if (!filter)
315     return 1;
316
317   for (d = b->first_dev; d; d = d->bus_next)
318     if (check_dev_filter(filter, d))
319       return 1;
320
321   return 0;
322 }
323
324 static void
325 show_tree_bus(struct pci_filter *filter, struct bus *b, char *line, char *p)
326 {
327   if (!b->first_dev)
328     print_it(line, p);
329   else if (!b->first_dev->bus_next)
330     {
331       if (check_dev_filter(filter, b->first_dev))
332         {
333           p = tree_printf(line, p, "--");
334           show_tree_dev(filter, b->first_dev, line, p);
335         }
336       else
337         print_it(line, p);
338     }
339   else
340     {
341       int empty = 1;
342       struct device *d = b->first_dev;
343       while (d->bus_next)
344         {
345           if (check_dev_filter(filter, d))
346             {
347               char *p2 = tree_printf(line, p, "+-");
348               show_tree_dev(filter, d, line, p2);
349               empty = 0;
350             }
351           d = d->bus_next;
352         }
353       if (check_dev_filter(filter, d))
354         {
355           p = tree_printf(line, p, "\\-");
356           show_tree_dev(filter, d, line, p);
357           empty = 0;
358         }
359       if (empty)
360         print_it(line, p);
361     }
362 }
363
364 static void
365 show_tree_bridge(struct pci_filter *filter, struct bridge *b, char *line, char *p)
366 {
367   *p++ = '-';
368   if (!b->first_bus->sibling)
369     {
370       if (check_bus_filter(filter, b->first_bus))
371         {
372           if (b == &host_bridge)
373             p = tree_printf(line, p, "[%04x:%02x]-", b->domain, b->first_bus->number);
374           show_tree_bus(filter, b->first_bus, line, p);
375         }
376       else
377         print_it(line, p);
378     }
379   else
380     {
381       int empty = 1;
382       struct bus *u = b->first_bus;
383       char *k;
384
385       while (u->sibling)
386         {
387           if (check_bus_filter(filter, u))
388             {
389               k = tree_printf(line, p, "+-[%04x:%02x]-", u->domain, u->number);
390               show_tree_bus(filter, u, line, k);
391               empty = 0;
392             }
393           u = u->sibling;
394         }
395       if (check_bus_filter(filter, u))
396         {
397           k = tree_printf(line, p, "\\-[%04x:%02x]-", u->domain, u->number);
398           show_tree_bus(filter, u, line, k);
399           empty = 0;
400         }
401       if (empty)
402         print_it(line, p);
403     }
404 }
405
406 void
407 show_forest(struct pci_filter *filter)
408 {
409   char line[LINE_BUF_SIZE];
410   show_tree_bridge(filter, &host_bridge, line, line);
411 }