]> mj.ucw.cz Git - pciutils.git/blob - ls-tree.c
CXL: Capability vendor ID changed
[pciutils.git] / ls-tree.c
1 /*
2  *      The PCI Utilities -- Show Bus Tree
3  *
4  *      Copyright (c) 1997--2020 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, 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 bus *
29 new_bus(struct bridge *b, unsigned int domain, unsigned int n)
30 {
31   struct bus *bus = xmalloc(sizeof(struct bus));
32   bus->domain = domain;
33   bus->number = n;
34   bus->sibling = b->first_bus;
35   bus->first_dev = NULL;
36   bus->last_dev = &bus->first_dev;
37   bus->parent_bridge = b;
38   b->first_bus = bus;
39   return bus;
40 }
41
42 static void
43 insert_dev(struct device *d, struct bridge *b)
44 {
45   struct pci_dev *p = d->dev;
46   struct bus *bus;
47
48   if (! (bus = find_bus(b, p->domain, p->bus)))
49     {
50       struct bridge *c;
51       for (c=b->child; c; c=c->next)
52         if (c->domain == (unsigned)p->domain && c->secondary <= p->bus && p->bus <= c->subordinate)
53           {
54             insert_dev(d, c);
55             return;
56           }
57       bus = new_bus(b, p->domain, p->bus);
58     }
59   /* Simple insertion at the end _does_ guarantee the correct order as the
60    * original device list was sorted by (domain, bus, devfn) lexicographically
61    * and all devices on the new list have the same bus number.
62    */
63   *bus->last_dev = d;
64   bus->last_dev = &d->bus_next;
65   d->bus_next = NULL;
66   d->parent_bus = bus;
67 }
68
69 void
70 grow_tree(void)
71 {
72   struct device *d;
73   struct bridge **last_br, *b;
74
75   /* Build list of bridges */
76
77   last_br = &host_bridge.chain;
78   for (d=first_dev; d; d=d->next)
79     {
80       struct pci_dev *dd = d->dev;
81       word class = dd->device_class;
82       byte ht = get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f;
83       if ((class >> 8) == PCI_BASE_CLASS_BRIDGE &&
84           (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS))
85         {
86           b = xmalloc(sizeof(struct bridge));
87           b->domain = dd->domain;
88           if (ht == PCI_HEADER_TYPE_BRIDGE)
89             {
90               b->primary = get_conf_byte(d, PCI_PRIMARY_BUS);
91               b->secondary = get_conf_byte(d, PCI_SECONDARY_BUS);
92               b->subordinate = get_conf_byte(d, PCI_SUBORDINATE_BUS);
93             }
94           else
95             {
96               b->primary = get_conf_byte(d, PCI_CB_PRIMARY_BUS);
97               b->secondary = get_conf_byte(d, PCI_CB_CARD_BUS);
98               b->subordinate = get_conf_byte(d, PCI_CB_SUBORDINATE_BUS);
99             }
100           *last_br = b;
101           last_br = &b->chain;
102           b->next = b->child = NULL;
103           b->first_bus = NULL;
104           b->br_dev = d;
105           d->bridge = b;
106           pacc->debug("Tree: bridge %04x:%02x:%02x.%d: %02x -> %02x-%02x\n",
107             dd->domain, dd->bus, dd->dev, dd->func,
108             b->primary, b->secondary, b->subordinate);
109         }
110     }
111   *last_br = NULL;
112
113   /* Create a bridge tree */
114
115   for (b=&host_bridge; b; b=b->chain)
116     {
117       struct bridge *c, *best;
118       best = NULL;
119       for (c=&host_bridge; c; c=c->chain)
120         if (c != b && (c == &host_bridge || b->domain == c->domain) &&
121             b->primary >= c->secondary && b->primary <= c->subordinate &&
122             (!best || best->subordinate - best->primary > c->subordinate - c->primary))
123           best = c;
124       if (best)
125         {
126           b->next = best->child;
127           best->child = b;
128         }
129     }
130
131   /* Insert secondary bus for each bridge */
132
133   for (b=&host_bridge; b; b=b->chain)
134     if (!find_bus(b, b->domain, b->secondary))
135       new_bus(b, b->domain, b->secondary);
136
137   /* Create bus structs and link devices */
138
139   for (d=first_dev; d; d=d->next)
140     insert_dev(d, &host_bridge);
141 }
142
143 static void
144 print_it(char *line, char *p)
145 {
146   *p++ = '\n';
147   *p = 0;
148   fputs(line, stdout);
149   for (p=line; *p; p++)
150     if (*p == '+' || *p == '|')
151       *p = '|';
152     else
153       *p = ' ';
154 }
155
156 static void show_tree_bridge(struct bridge *, char *, char *);
157
158 #define LINE_BUF_SIZE 1024
159
160 static char * FORMAT_CHECK(printf, 3, 4)
161 tree_printf(char *line, char *p, char *fmt, ...)
162 {
163   va_list args;
164   char *end = line + LINE_BUF_SIZE - 2;
165
166   if (p >= end)
167     return p;
168
169   va_start(args, fmt);
170   int res = vsnprintf(p, end - p, fmt, args);
171   if (res < 0)
172     {
173       /* Ancient C libraries return -1 on overflow */
174       p += strlen(p);
175     }
176   else
177     p += res;
178
179   va_end(args);
180   return p;
181 }
182
183 static void
184 show_tree_dev(struct device *d, char *line, char *p)
185 {
186   struct pci_dev *q = d->dev;
187   struct bridge *b;
188   char namebuf[256];
189
190   p = tree_printf(line, p, "%02x.%x", q->dev, q->func);
191   for (b=&host_bridge; b; b=b->chain)
192     if (b->br_dev == d)
193       {
194         if (b->secondary == b->subordinate)
195           p = tree_printf(line, p, "-[%02x]-", b->secondary);
196         else
197           p = tree_printf(line, p, "-[%02x-%02x]-", b->secondary, b->subordinate);
198         show_tree_bridge(b, line, p);
199         return;
200       }
201   if (verbose)
202     p = tree_printf(line, p, "  %s",
203                     pci_lookup_name(pacc, namebuf, sizeof(namebuf),
204                                     PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE,
205                                     q->vendor_id, q->device_id));
206   print_it(line, p);
207 }
208
209 static void
210 show_tree_bus(struct bus *b, char *line, char *p)
211 {
212   if (!b->first_dev)
213     print_it(line, p);
214   else if (!b->first_dev->bus_next)
215     {
216       p = tree_printf(line, p, "--");
217       show_tree_dev(b->first_dev, line, p);
218     }
219   else
220     {
221       struct device *d = b->first_dev;
222       while (d->bus_next)
223         {
224           char *p2 = tree_printf(line, p, "+-");
225           show_tree_dev(d, line, p2);
226           d = d->bus_next;
227         }
228       p = tree_printf(line, p, "\\-");
229       show_tree_dev(d, line, p);
230     }
231 }
232
233 static void
234 show_tree_bridge(struct bridge *b, char *line, char *p)
235 {
236   *p++ = '-';
237   if (!b->first_bus->sibling)
238     {
239       if (b == &host_bridge)
240         p = tree_printf(line, p, "[%04x:%02x]-", b->domain, b->first_bus->number);
241       show_tree_bus(b->first_bus, line, p);
242     }
243   else
244     {
245       struct bus *u = b->first_bus;
246       char *k;
247
248       while (u->sibling)
249         {
250           k = tree_printf(line, p, "+-[%04x:%02x]-", u->domain, u->number);
251           show_tree_bus(u, line, k);
252           u = u->sibling;
253         }
254       k = tree_printf(line, p, "\\-[%04x:%02x]-", u->domain, u->number);
255       show_tree_bus(u, line, k);
256     }
257 }
258
259 void
260 show_forest(struct pci_filter *filter)
261 {
262   char line[LINE_BUF_SIZE];
263   if (filter == NULL)
264     show_tree_bridge(&host_bridge, line, line);
265   else
266     {
267       struct bridge *b;
268       for (b=&host_bridge; b; b=b->chain)
269         {
270           if (b->br_dev && pci_filter_match(filter, b->br_dev->dev))
271             {
272                 struct pci_dev *d = b->br_dev->dev;
273                 char *p = line;
274                 p = tree_printf(line, p, "%04x:%02x:", d->domain_16, d->bus);
275                 show_tree_dev(b->br_dev, line, p);
276             }
277         }
278     }
279 }