/*****************************************************************************/ /* * devtree.c -- USB device tree. * * Copyright (C) 1999 Thomas Sailer, sailer@ife.ee.ethz.ch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Please note that the GPL allows you to use the driver, NOT the radio. * In order to use the radio, you need a license from the communications * authority of your country. * */ /*****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "devtree.h" /* ---------------------------------------------------------------------- */ LIST_HEAD(usbbuslist); /* ---------------------------------------------------------------------- */ static void freedev(struct usbdevnode *dev) { free(dev); } static void freebus(struct usbbusnode *bus) { free(bus); } /* ---------------------------------------------------------------------- */ static void markdel(struct list_head *list) { struct usbdevnode *dev; struct list_head *list2; for (list2 = list->next; list2 != list; list2 = list2->next) { dev = list_entry(list2, struct usbdevnode, list); dev->flags |= USBFLG_DELETED; markdel(&dev->childlist); } } void devtree_markdeleted(void) { struct usbbusnode *bus; struct list_head *list; for(list = usbbuslist.next; list != &usbbuslist; list = list->next) { bus = list_entry(list, struct usbbusnode, list); markdel(&bus->childlist); } } struct usbbusnode *devtree_findbus(unsigned int busn) { struct usbbusnode *bus; struct list_head *list; for(list = usbbuslist.next; list != &usbbuslist; list = list->next) { bus = list_entry(list, struct usbbusnode, list); if (bus->busnum == busn) return bus; } return NULL; } static struct usbdevnode *findsubdevice(struct list_head *list, unsigned int devn) { struct usbdevnode *dev, *dev2; struct list_head *list2; for (list2 = list->next; list2 != list; list2 = list2->next) { dev = list_entry(list2, struct usbdevnode, list); if (dev->devnum == devn) return dev; dev2 = findsubdevice(&dev->childlist, devn); if (dev2) return dev2; } return NULL; } struct usbdevnode *devtree_finddevice(struct usbbusnode *bus, unsigned int devn) { return findsubdevice(&bus->childlist, devn); } /* ---------------------------------------------------------------------- */ void devtree_parsedevfile(int fd) { char buf[16384]; char *start, *end, *lineend, *cp; int ret; unsigned int devnum = 0, busnum = 0, parentdevnum = 0, level = 0; unsigned int class = 0xff, vendor = 0xffff, prodid = 0xffff, speed = 0; struct usbbusnode *bus; struct usbdevnode *dev, *dev2; devtree_markdeleted(); if (lseek(fd, 0, SEEK_SET) == (off_t)-1) lprintf(0, "lseek: %s (%d)\n", strerror(errno), errno); ret = read(fd, buf, sizeof(buf)-1); if (ret == -1) lprintf(0, "read: %s (%d)\n", strerror(errno), errno); end = buf + ret; *end = 0; start = buf; while (start < end) { lineend = strchr(start, '\n'); if (!lineend) break; *lineend = 0; switch (start[0]) { case 'T': /* topology line */ if ((cp = strstr(start, "Dev#="))) { devnum = strtoul(cp + 5, NULL, 0); } else devnum = 0; if ((cp = strstr(start, "Bus="))) { busnum = strtoul(cp + 4, NULL, 0); } else busnum = 0; if ((cp = strstr(start, "Prnt="))) { parentdevnum = strtoul(cp + 5, NULL, 0); } else parentdevnum = 0; if ((cp = strstr(start, "Lev="))) { level = strtoul(cp + 4, NULL, 0); } else level = 0; if (strstr(start, "Spd=1.5")) speed = 1; else if (strstr(start, "Spd=12")) speed = 2; else speed = 0; break; case 'D': if ((cp = strstr(start, "Cls="))) { class = strtoul(cp + 4, NULL, 16); } else class = 0xff; break; case 'P': if ((cp = strstr(start, "Vendor="))) { vendor = strtoul(cp + 7, NULL, 16); } else vendor = 0xffff; if ((cp = strstr(start, "ProdID="))) { prodid = strtoul(cp + 7, NULL, 16); } else prodid = 0xffff; /* print device */ #if 0 printf("Device %3d Vendor %04x Product ID %04x Class %02x Speed %s\n", devnum, vendor, prodid, class, speed == 2 ? "12 MBPS" : speed == 1 ? "1.5 MBPS" : "unknown"); #endif if (!(bus = devtree_findbus(busnum))) { if (!(bus = malloc(sizeof(struct usbbusnode)))) lprintf(0, "Out of memory\n"); bus->busnum = busnum; bus->flags = USBFLG_NEW; INIT_LIST_HEAD(&bus->childlist); list_add_tail(&bus->list, &usbbuslist); } else { bus->flags &= ~USBFLG_DELETED; } if (!(dev = devtree_finddevice(bus, devnum)) || dev->vendorid != vendor || dev->productid != prodid) { if (!(dev = malloc(sizeof(struct usbdevnode)))) lprintf(0, "Out of memory\n"); dev->devnum = devnum; dev->flags = USBFLG_NEW; dev->bus = bus; dev->vendorid = vendor; dev->productid = prodid; INIT_LIST_HEAD(&dev->childlist); if (level == 0 && parentdevnum == 0) { list_add_tail(&dev->list, &bus->childlist); dev->parent = NULL; } else { if (!(dev2 = devtree_finddevice(bus, parentdevnum))) lprintf(0, "Bus %d Device %d Parent Device %d not found\n", busnum, devnum, parentdevnum); dev->parent = dev2; list_add_tail(&dev->list, &dev2->childlist); } } else { dev->flags &= ~USBFLG_DELETED; } break; default: break; } #if 0 printf("line: %s\n", start); #endif start = lineend + 1; } } /* ---------------------------------------------------------------------- */ static void deletetree(struct list_head *list, unsigned int force) { struct usbdevnode *dev; struct list_head *list2; for (list2 = list->next; list2 != list;) { dev = list_entry(list2, struct usbdevnode, list); list2 = list2->next; deletetree(&dev->childlist, force || dev->flags & USBFLG_DELETED); if (!force && !(dev->flags & USBFLG_DELETED)) continue; list_del(&dev->list); INIT_LIST_HEAD(&dev->list); devtree_devdisconnect(dev); freedev(dev); } } static void newtree(struct list_head *list) { struct usbdevnode *dev; struct list_head *list2; for (list2 = list->next; list2 != list; list2 = list2->next) { dev = list_entry(list2, struct usbdevnode, list); if (dev->flags & USBFLG_NEW) devtree_devconnect(dev); dev->flags &= ~USBFLG_NEW; newtree(&dev->childlist); } } void devtree_processchanges(void) { struct list_head *list; struct usbbusnode *bus; for (list = usbbuslist.next; list != &usbbuslist;) { bus = list_entry(list, struct usbbusnode, list); list = list->next; deletetree(&bus->childlist, bus->flags & USBFLG_DELETED); if (!(bus->flags & USBFLG_DELETED)) continue; list_del(&bus->list); INIT_LIST_HEAD(&bus->list); devtree_busdisconnect(bus); freebus(bus); } for (list = usbbuslist.next; list != &usbbuslist; list = list->next) { bus = list_entry(list, struct usbbusnode, list); if (bus->flags & USBFLG_NEW) devtree_busconnect(bus); bus->flags &= ~USBFLG_NEW; newtree(&bus->childlist); } } /* ---------------------------------------------------------------------- */ static void dumpdevlist(struct list_head *list, unsigned int level, unsigned int mask) { struct usbdevnode *dev; struct list_head *list2; char buf[512]; char *cp; unsigned int i; for (list2 = list->next; list2 != list; ) { dev = list_entry(list2, struct usbdevnode, list); list2 = list2->next; for (cp = buf, i = 0; i < level; i++) { *cp++ = (mask & (1 << i)) ? '|' : ' '; *cp++ = ' '; } if (list2 != list) { mask |= (1 << level); *cp++ = '|'; } else { mask &= ~(1 << level); *cp++ = '`'; } *cp++ = '-'; snprintf(cp, buf + sizeof(buf) - cp, "Dev# %3d Vendor 0x%04x Product 0x%04x", dev->devnum, dev->vendorid, dev->productid); lprintf(1, "%s\n", buf); dumpdevlist(&dev->childlist, level+1, mask); } } void devtree_dump(void) { struct list_head *list; struct usbbusnode *bus; for (list = usbbuslist.next; list != &usbbuslist; list = list->next) { bus = list_entry(list, struct usbbusnode, list); lprintf(1, "Bus# %2d\n", bus->busnum); dumpdevlist(&bus->childlist, 0, 0); } }