blob: a3cb90099be15a41495284b9199a6a09408a8398 [file] [log] [blame]
/*****************************************************************************/
/*
* 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 <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#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, 10);
} else
busnum = 0;
if ((cp = strstr(start, "Prnt="))) {
parentdevnum = strtoul(cp + 5, NULL, 10);
} else
parentdevnum = 0;
if ((cp = strstr(start, "Lev="))) {
level = strtoul(cp + 4, NULL, 10);
} 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);
}
}