Update to release 005.
[framework/system/usbutils.git] / devtree.c
1 /*****************************************************************************/
2
3 /*
4  *      devtree.c  --  USB device tree.
5  *
6  *      Copyright (C) 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
7  *
8  *      This program is free software; you can redistribute it and/or modify
9  *      it under the terms of the GNU General Public License as published by
10  *      the Free Software Foundation; either version 2 of the License, or
11  *      (at your option) any later version.
12  *
13  *      This program is distributed in the hope that it will be useful,
14  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *      GNU General Public License for more details.
17  *
18  */
19
20 /*****************************************************************************/
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <sys/types.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "names.h"
34 #include "devtree.h"
35
36 /* ---------------------------------------------------------------------- */
37
38 LIST_HEAD(usbbuslist);
39
40 /* ---------------------------------------------------------------------- */
41
42 static void freedev(struct usbdevnode *dev)
43 {
44         free(dev);
45 }
46
47 static void freebus(struct usbbusnode *bus)
48 {
49         free(bus);
50 }
51
52 /* ---------------------------------------------------------------------- */
53
54 static void markdel(struct list_head *list)
55 {
56         struct usbdevnode *dev;
57         struct list_head *list2;
58
59         for (list2 = list->next; list2 != list; list2 = list2->next) {
60                 dev = list_entry(list2, struct usbdevnode, list);
61                 dev->flags |= USBFLG_DELETED;
62                 markdel(&dev->childlist);
63         }
64 }
65
66 void devtree_markdeleted(void)
67 {
68         struct usbbusnode *bus;
69         struct list_head *list;
70
71         for (list = usbbuslist.next; list != &usbbuslist; list = list->next) {
72                 bus = list_entry(list, struct usbbusnode, list);
73                 markdel(&bus->childlist);
74         }
75 }
76
77 struct usbbusnode *devtree_findbus(unsigned int busn)
78 {
79         struct usbbusnode *bus;
80         struct list_head *list;
81
82         for (list = usbbuslist.next; list != &usbbuslist; list = list->next) {
83                 bus = list_entry(list, struct usbbusnode, list);
84                 if (bus->busnum == busn)
85                         return bus;
86         }
87         return NULL;
88 }
89
90 static struct usbdevnode *findsubdevice(struct list_head *list, unsigned int devn)
91 {
92         struct usbdevnode *dev, *dev2;
93         struct list_head *list2;
94
95         for (list2 = list->next; list2 != list; list2 = list2->next) {
96                 dev = list_entry(list2, struct usbdevnode, list);
97                 if (dev->devnum == devn)
98                         return dev;
99                 dev2 = findsubdevice(&dev->childlist, devn);
100                 if (dev2)
101                         return dev2;
102         }
103         return NULL;
104 }
105
106 struct usbdevnode *devtree_finddevice(struct usbbusnode *bus, unsigned int devn)
107 {
108         return findsubdevice(&bus->childlist, devn);
109 }
110
111 /* ---------------------------------------------------------------------- */
112
113 void devtree_parsedevfile(int fd)
114 {
115         char buf[16384];
116         char *start, *end, *lineend, *cp;
117         int ret;
118         unsigned int devnum = 0, busnum = 0, parentdevnum = 0, level = 0;
119         unsigned int class = 0xff, vendor = 0xffff, prodid = 0xffff, speed = 0;
120         struct usbbusnode *bus;
121         struct usbdevnode *dev, *dev2;
122
123         devtree_markdeleted();
124         if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
125                 lprintf(0, "lseek: %s (%d)\n", strerror(errno), errno);
126         ret = read(fd, buf, sizeof(buf)-1);
127         if (ret == -1)
128                 lprintf(0, "read: %s (%d)\n", strerror(errno), errno);
129         end = buf + ret;
130         *end = 0;
131         start = buf;
132         while (start < end) {
133                 lineend = strchr(start, '\n');
134                 if (!lineend)
135                         break;
136                 *lineend = 0;
137                 switch (start[0]) {
138                 case 'T':  /* topology line */
139                         if ((cp = strstr(start, "Dev#=")))
140                                 devnum = strtoul(cp + 5, NULL, 0);
141                         else
142                                 devnum = 0;
143                         if ((cp = strstr(start, "Bus=")))
144                                 busnum = strtoul(cp + 4, NULL, 10);
145                         else
146                                 busnum = 0;
147                         if ((cp = strstr(start, "Prnt=")))
148                                 parentdevnum = strtoul(cp + 5, NULL, 10);
149                         else
150                                 parentdevnum = 0;
151                         if ((cp = strstr(start, "Lev=")))
152                                 level = strtoul(cp + 4, NULL, 10);
153                         else
154                                 level = 0;
155                         if (strstr(start, "Spd=1.5"))
156                                 speed = 1;
157                         else if (strstr(start, "Spd=12"))
158                                 speed = 2;
159                         else
160                                 speed = 0;
161                         break;
162
163                 case 'D':
164                         if ((cp = strstr(start, "Cls=")))
165                                 class = strtoul(cp + 4, NULL, 16);
166                         else
167                                 class = 0xff;
168                         break;
169
170                 case 'P':
171                         if ((cp = strstr(start, "Vendor=")))
172                                 vendor = strtoul(cp + 7, NULL, 16);
173                         else
174                                 vendor = 0xffff;
175                         if ((cp = strstr(start, "ProdID=")))
176                                 prodid = strtoul(cp + 7, NULL, 16);
177                         else
178                                 prodid = 0xffff;
179                         /* print device */
180 #if 0
181                         printf("Device %3d Vendor %04x Product ID %04x Class %02x Speed %s\n",
182                                devnum, vendor, prodid, class, speed == 2 ? "12 MBPS" : speed == 1 ? "1.5 MBPS" : "unknown");
183 #endif
184                         if (!(bus = devtree_findbus(busnum))) {
185                                 if (!(bus = malloc(sizeof(struct usbbusnode))))
186                                         lprintf(0, "Out of memory\n");
187                                 bus->busnum = busnum;
188                                 bus->flags = USBFLG_NEW;
189                                 INIT_LIST_HEAD(&bus->childlist);
190                                 list_add_tail(&bus->list, &usbbuslist);
191                         } else {
192                                 bus->flags &= ~USBFLG_DELETED;
193                         }
194                         if (!(dev = devtree_finddevice(bus, devnum)) || dev->vendorid != vendor || dev->productid != prodid) {
195                                 if (!(dev = malloc(sizeof(struct usbdevnode))))
196                                         lprintf(0, "Out of memory\n");
197                                 dev->devnum = devnum;
198                                 dev->flags = USBFLG_NEW;
199                                 dev->bus = bus;
200                                 dev->vendorid = vendor;
201                                 dev->productid = prodid;
202                                 INIT_LIST_HEAD(&dev->childlist);
203                                 if (level == 0 && parentdevnum == 0) {
204                                         list_add_tail(&dev->list, &bus->childlist);
205                                         dev->parent = NULL;
206                                 } else {
207                                         if (!(dev2 = devtree_finddevice(bus, parentdevnum)))
208                                                 lprintf(0, "Bus %d Device %d Parent Device %d not found\n", busnum, devnum, parentdevnum);
209                                         dev->parent = dev2;
210                                         list_add_tail(&dev->list, &dev2->childlist);
211                                 }
212                         } else {
213                                 dev->flags &= ~USBFLG_DELETED;
214                         }
215                         break;
216
217                 default:
218                         break;
219                 }
220 #if 0
221                 printf("line: %s\n", start);
222 #endif
223                 start = lineend + 1;
224         }
225 }
226
227 /* ---------------------------------------------------------------------- */
228
229 static void deletetree(struct list_head *list, unsigned int force)
230 {
231         struct usbdevnode *dev;
232         struct list_head *list2;
233
234         for (list2 = list->next; list2 != list;) {
235                 dev = list_entry(list2, struct usbdevnode, list);
236                 list2 = list2->next;
237                 deletetree(&dev->childlist,
238                            force || dev->flags & USBFLG_DELETED);
239                 if (!force && !(dev->flags & USBFLG_DELETED))
240                         continue;
241                 list_del(&dev->list);
242                 INIT_LIST_HEAD(&dev->list);
243                 devtree_devdisconnect(dev);
244                 freedev(dev);
245         }
246 }
247
248 static void newtree(struct list_head *list)
249 {
250         struct usbdevnode *dev;
251         struct list_head *list2;
252
253         for (list2 = list->next; list2 != list; list2 = list2->next) {
254                 dev = list_entry(list2, struct usbdevnode, list);
255                 if (dev->flags & USBFLG_NEW)
256                         devtree_devconnect(dev);
257                 dev->flags &= ~USBFLG_NEW;
258                 newtree(&dev->childlist);
259         }
260 }
261
262 void devtree_processchanges(void)
263 {
264         struct list_head *list;
265         struct usbbusnode *bus;
266
267         for (list = usbbuslist.next; list != &usbbuslist;) {
268                 bus = list_entry(list, struct usbbusnode, list);
269                 list = list->next;
270                 deletetree(&bus->childlist, bus->flags & USBFLG_DELETED);
271                 if (!(bus->flags & USBFLG_DELETED))
272                         continue;
273                 list_del(&bus->list);
274                 INIT_LIST_HEAD(&bus->list);
275                 devtree_busdisconnect(bus);
276                 freebus(bus);
277         }
278         for (list = usbbuslist.next; list != &usbbuslist; list = list->next) {
279                 bus = list_entry(list, struct usbbusnode, list);
280                 if (bus->flags & USBFLG_NEW)
281                         devtree_busconnect(bus);
282                 bus->flags &= ~USBFLG_NEW;
283                 newtree(&bus->childlist);
284         }
285 }
286
287 /* ---------------------------------------------------------------------- */
288
289 static void dumpdevlist(struct list_head *list, unsigned int level,
290                         unsigned int mask, unsigned int verblevel)
291 {
292         struct usbdevnode *dev;
293         struct list_head *list2;
294         char vendor[128];
295         char product[128];
296         char buf[512];
297         char *cp;
298         unsigned int i;
299
300         for (list2 = list->next; list2 != list; ) {
301                 dev = list_entry(list2, struct usbdevnode, list);
302                 list2 = list2->next;
303                 for (cp = buf, i = 0; i < level; i++) {
304                         *cp++ = (mask & (1 << i)) ? '|' : ' ';
305                         *cp++ = ' ';
306                 }
307                 if (list2 != list) {
308                         mask |= (1 << level);
309                         *cp++ = '|';
310                 } else {
311                         mask &= ~(1 << level);
312                         *cp++ = '`';
313                 }
314                 *cp++ = '-';
315                 if (verblevel > 1) {
316                         get_vendor_string(vendor, sizeof(vendor), dev->vendorid);
317                         get_product_string(product, sizeof(product), dev->vendorid, dev->productid);
318                         snprintf(cp, buf + sizeof(buf) - cp,
319                                 "Dev# %3d Vendor 0x%04x Product 0x%04x %s %s",
320                                 dev->devnum, dev->vendorid, dev->productid, vendor, product);
321                 } else {
322                         snprintf(cp, buf + sizeof(buf) - cp,
323                                  "Dev# %3d Vendor 0x%04x Product 0x%04x",
324                                  dev->devnum, dev->vendorid, dev->productid);
325                 }
326                 lprintf(1, "%s\n", buf);
327                 dumpdevlist(&dev->childlist, level+1, mask, verblevel);
328         }
329 }
330
331 void devtree_dump(unsigned int verblevel)
332 {
333         struct list_head *list;
334         struct usbbusnode *bus;
335
336         for (list = usbbuslist.next; list != &usbbuslist; list = list->next) {
337                 bus = list_entry(list, struct usbbusnode, list);
338                 lprintf(1, "Bus# %2d\n", bus->busnum);
339                 dumpdevlist(&bus->childlist, 0, 0, verblevel);
340         }
341 }