blob: 9a174c1cc55a16be64ba0a8af4345a95d2ac3fe7 [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.
*
*/
/*****************************************************************************/
#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 "names.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, unsigned int verblevel)
{
struct usbdevnode *dev;
struct list_head *list2;
char vendor[128];
char product[128];
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++ = '-';
if (verblevel > 1) {
get_vendor_string(vendor, sizeof(vendor), dev->vendorid);
get_product_string(product, sizeof(product), dev->vendorid, dev->productid);
snprintf(cp, buf + sizeof(buf) - cp,
"Dev# %3d Vendor 0x%04x Product 0x%04x %s %s",
dev->devnum, dev->vendorid, dev->productid, vendor, product);
} else {
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, verblevel);
}
}
void devtree_dump(unsigned int verblevel)
{
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, verblevel);
}
}