diff options
Diffstat (limited to 'drivers/staging/usbip/userspace/libsrc')
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/Makefile.am | 7 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/names.c | 504 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/names.h | 41 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/usbip_common.c | 292 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/usbip_common.h | 150 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c | 401 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h | 48 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/vhci_driver.c | 537 | ||||
| -rw-r--r-- | drivers/staging/usbip/userspace/libsrc/vhci_driver.h | 67 |
9 files changed, 2047 insertions, 0 deletions
diff --git a/drivers/staging/usbip/userspace/libsrc/Makefile.am b/drivers/staging/usbip/userspace/libsrc/Makefile.am new file mode 100644 index 00000000..4921189e --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/Makefile.am @@ -0,0 +1,7 @@ +libusbip_la_CPPFLAGS = -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"' +libusbip_la_CFLAGS = @EXTRA_CFLAGS@ +libusbip_la_LDFLAGS = -version-info @LIBUSBIP_VERSION@ + +lib_LTLIBRARIES := libusbip.la +libusbip_la_SOURCES := names.c names.h usbip_host_driver.c usbip_host_driver.h \ + usbip_common.c usbip_common.h vhci_driver.c vhci_driver.h diff --git a/drivers/staging/usbip/userspace/libsrc/names.c b/drivers/staging/usbip/userspace/libsrc/names.c new file mode 100644 index 00000000..3c8d28b7 --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/names.c @@ -0,0 +1,504 @@ +/* + * names.c -- USB name database manipulation routines + * + * Copyright (C) 1999, 2000 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. + * + * + * + * + * + * Copyright (C) 2005 Takahiro Hirofuchi + * - names_deinit() is added. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> + +#include "names.h" +#include "usbip_common.h" + +struct vendor { + struct vendor *next; + u_int16_t vendorid; + char name[1]; +}; + +struct product { + struct product *next; + u_int16_t vendorid, productid; + char name[1]; +}; + +struct class { + struct class *next; + u_int8_t classid; + char name[1]; +}; + +struct subclass { + struct subclass *next; + u_int8_t classid, subclassid; + char name[1]; +}; + +struct protocol { + struct protocol *next; + u_int8_t classid, subclassid, protocolid; + char name[1]; +}; + +struct genericstrtable { + struct genericstrtable *next; + unsigned int num; + char name[1]; +}; + + +#define HASH1 0x10 +#define HASH2 0x02 +#define HASHSZ 16 + +static unsigned int hashnum(unsigned int num) +{ + unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27; + + for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1) + if (num & mask1) + num ^= mask2; + return num & (HASHSZ-1); +} + + +static struct vendor *vendors[HASHSZ] = { NULL, }; +static struct product *products[HASHSZ] = { NULL, }; +static struct class *classes[HASHSZ] = { NULL, }; +static struct subclass *subclasses[HASHSZ] = { NULL, }; +static struct protocol *protocols[HASHSZ] = { NULL, }; + +const char *names_vendor(u_int16_t vendorid) +{ + struct vendor *v; + + v = vendors[hashnum(vendorid)]; + for (; v; v = v->next) + if (v->vendorid == vendorid) + return v->name; + return NULL; +} + +const char *names_product(u_int16_t vendorid, u_int16_t productid) +{ + struct product *p; + + p = products[hashnum((vendorid << 16) | productid)]; + for (; p; p = p->next) + if (p->vendorid == vendorid && p->productid == productid) + return p->name; + return NULL; +} + +const char *names_class(u_int8_t classid) +{ + struct class *c; + + c = classes[hashnum(classid)]; + for (; c; c = c->next) + if (c->classid == classid) + return c->name; + return NULL; +} + +const char *names_subclass(u_int8_t classid, u_int8_t subclassid) +{ + struct subclass *s; + + s = subclasses[hashnum((classid << 8) | subclassid)]; + for (; s; s = s->next) + if (s->classid == classid && s->subclassid == subclassid) + return s->name; + return NULL; +} + +const char *names_protocol(u_int8_t classid, u_int8_t subclassid, + u_int8_t protocolid) +{ + struct protocol *p; + + p = protocols[hashnum((classid << 16) | (subclassid << 8) + | protocolid)]; + for (; p; p = p->next) + if (p->classid == classid && p->subclassid == subclassid && + p->protocolid == protocolid) + return p->name; + return NULL; +} + +/* add a cleanup function by takahiro */ +struct pool { + struct pool *next; + void *mem; +}; + +static struct pool *pool_head; + +static void *my_malloc(size_t size) +{ + struct pool *p; + + p = calloc(1, sizeof(struct pool)); + if (!p) { + free(p); + return NULL; + } + + p->mem = calloc(1, size); + if (!p->mem) + return NULL; + + p->next = pool_head; + pool_head = p; + + return p->mem; +} + +void names_free(void) +{ + struct pool *pool; + + if (!pool_head) + return; + + for (pool = pool_head; pool != NULL; ) { + struct pool *tmp; + + if (pool->mem) + free(pool->mem); + + tmp = pool; + pool = pool->next; + free(tmp); + } +} + +static int new_vendor(const char *name, u_int16_t vendorid) +{ + struct vendor *v; + unsigned int h = hashnum(vendorid); + + v = vendors[h]; + for (; v; v = v->next) + if (v->vendorid == vendorid) + return -1; + v = my_malloc(sizeof(struct vendor) + strlen(name)); + if (!v) + return -1; + strcpy(v->name, name); + v->vendorid = vendorid; + v->next = vendors[h]; + vendors[h] = v; + return 0; +} + +static int new_product(const char *name, u_int16_t vendorid, + u_int16_t productid) +{ + struct product *p; + unsigned int h = hashnum((vendorid << 16) | productid); + + p = products[h]; + for (; p; p = p->next) + if (p->vendorid == vendorid && p->productid == productid) + return -1; + p = my_malloc(sizeof(struct product) + strlen(name)); + if (!p) + return -1; + strcpy(p->name, name); + p->vendorid = vendorid; + p->productid = productid; + p->next = products[h]; + products[h] = p; + return 0; +} + +static int new_class(const char *name, u_int8_t classid) +{ + struct class *c; + unsigned int h = hashnum(classid); + + c = classes[h]; + for (; c; c = c->next) + if (c->classid == classid) + return -1; + c = my_malloc(sizeof(struct class) + strlen(name)); + if (!c) + return -1; + strcpy(c->name, name); + c->classid = classid; + c->next = classes[h]; + classes[h] = c; + return 0; +} + +static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid) +{ + struct subclass *s; + unsigned int h = hashnum((classid << 8) | subclassid); + + s = subclasses[h]; + for (; s; s = s->next) + if (s->classid == classid && s->subclassid == subclassid) + return -1; + s = my_malloc(sizeof(struct subclass) + strlen(name)); + if (!s) + return -1; + strcpy(s->name, name); + s->classid = classid; + s->subclassid = subclassid; + s->next = subclasses[h]; + subclasses[h] = s; + return 0; +} + +static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid, + u_int8_t protocolid) +{ + struct protocol *p; + unsigned int h = hashnum((classid << 16) | (subclassid << 8) + | protocolid); + + p = protocols[h]; + for (; p; p = p->next) + if (p->classid == classid && p->subclassid == subclassid + && p->protocolid == protocolid) + return -1; + p = my_malloc(sizeof(struct protocol) + strlen(name)); + if (!p) + return -1; + strcpy(p->name, name); + p->classid = classid; + p->subclassid = subclassid; + p->protocolid = protocolid; + p->next = protocols[h]; + protocols[h] = p; + return 0; +} + +static void parse(FILE *f) +{ + char buf[512], *cp; + unsigned int linectr = 0; + int lastvendor = -1; + int lastclass = -1; + int lastsubclass = -1; + int lasthut = -1; + int lastlang = -1; + unsigned int u; + + while (fgets(buf, sizeof(buf), f)) { + linectr++; + /* remove line ends */ + cp = strchr(buf, '\r'); + if (cp) + *cp = 0; + cp = strchr(buf, '\n'); + if (cp) + *cp = 0; + if (buf[0] == '#' || !buf[0]) + continue; + cp = buf; + if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' && + buf[3] == 'S' && buf[4] == 'D' && + buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/ + buf[7] == ' ') { + continue; + } + if (buf[0] == 'P' && buf[1] == 'H' && + buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') { + continue; + } + if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' && + buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') { + continue; + } + if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') { + lasthut = lastclass = lastvendor = lastsubclass = -1; + /* + * set 1 as pseudo-id to indicate that the parser is + * in a `L' section. + */ + lastlang = 1; + continue; + } + if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') { + /* class spec */ + cp = buf+2; + while (isspace(*cp)) + cp++; + if (!isxdigit(*cp)) { + err("Invalid class spec at line %u", linectr); + continue; + } + u = strtoul(cp, &cp, 16); + while (isspace(*cp)) + cp++; + if (!*cp) { + err("Invalid class spec at line %u", linectr); + continue; + } + if (new_class(cp, u)) + err("Duplicate class spec at line %u class %04x %s", + linectr, u, cp); + dbg("line %5u class %02x %s", linectr, u, cp); + lasthut = lastlang = lastvendor = lastsubclass = -1; + lastclass = u; + continue; + } + if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) { + /* audio terminal type spec */ + continue; + } + if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C' + && isspace(buf[3])) { + /* HID Descriptor bCountryCode */ + continue; + } + if (isxdigit(*cp)) { + /* vendor */ + u = strtoul(cp, &cp, 16); + while (isspace(*cp)) + cp++; + if (!*cp) { + err("Invalid vendor spec at line %u", linectr); + continue; + } + if (new_vendor(cp, u)) + err("Duplicate vendor spec at line %u vendor %04x %s", + linectr, u, cp); + dbg("line %5u vendor %04x %s", linectr, u, cp); + lastvendor = u; + lasthut = lastlang = lastclass = lastsubclass = -1; + continue; + } + if (buf[0] == '\t' && isxdigit(buf[1])) { + /* product or subclass spec */ + u = strtoul(buf+1, &cp, 16); + while (isspace(*cp)) + cp++; + if (!*cp) { + err("Invalid product/subclass spec at line %u", + linectr); + continue; + } + if (lastvendor != -1) { + if (new_product(cp, lastvendor, u)) + err("Duplicate product spec at line %u product %04x:%04x %s", + linectr, lastvendor, u, cp); + dbg("line %5u product %04x:%04x %s", linectr, + lastvendor, u, cp); + continue; + } + if (lastclass != -1) { + if (new_subclass(cp, lastclass, u)) + err("Duplicate subclass spec at line %u class %02x:%02x %s", + linectr, lastclass, u, cp); + dbg("line %5u subclass %02x:%02x %s", linectr, + lastclass, u, cp); + lastsubclass = u; + continue; + } + if (lasthut != -1) { + /* do not store hut */ + continue; + } + if (lastlang != -1) { + /* do not store langid */ + continue; + } + err("Product/Subclass spec without prior Vendor/Class spec at line %u", + linectr); + continue; + } + if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) { + /* protocol spec */ + u = strtoul(buf+2, &cp, 16); + while (isspace(*cp)) + cp++; + if (!*cp) { + err("Invalid protocol spec at line %u", + linectr); + continue; + } + if (lastclass != -1 && lastsubclass != -1) { + if (new_protocol(cp, lastclass, lastsubclass, + u)) + err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s", + linectr, lastclass, lastsubclass, + u, cp); + dbg("line %5u protocol %02x:%02x:%02x %s", + linectr, lastclass, lastsubclass, u, cp); + continue; + } + err("Protocol spec without prior Class and Subclass spec at line %u", + linectr); + continue; + } + if (buf[0] == 'H' && buf[1] == 'I' && + buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') { + continue; + } + if (buf[0] == 'H' && buf[1] == 'U' && + buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') { + lastlang = lastclass = lastvendor = lastsubclass = -1; + /* + * set 1 as pseudo-id to indicate that the parser is + * in a `HUT' section. + */ + lasthut = 1; + continue; + } + if (buf[0] == 'R' && buf[1] == ' ') + continue; + + if (buf[0] == 'V' && buf[1] == 'T') + continue; + + err("Unknown line at line %u", linectr); + } +} + + +int names_init(char *n) +{ + FILE *f; + + f = fopen(n, "r"); + if (!f) + return errno; + + parse(f); + fclose(f); + return 0; +} diff --git a/drivers/staging/usbip/userspace/libsrc/names.h b/drivers/staging/usbip/userspace/libsrc/names.h new file mode 100644 index 00000000..68092651 --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/names.h @@ -0,0 +1,41 @@ +/* + * names.h -- USB name database manipulation routines + * + * Copyright (C) 1999, 2000 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. + * + * + * + * Copyright (C) 2005 Takahiro Hirofuchi + * - names_free() is added. + */ + +#ifndef _NAMES_H +#define _NAMES_H + +#include <sys/types.h> + +/* used by usbip_common.c */ +extern const char *names_vendor(u_int16_t vendorid); +extern const char *names_product(u_int16_t vendorid, u_int16_t productid); +extern const char *names_class(u_int8_t classid); +extern const char *names_subclass(u_int8_t classid, u_int8_t subclassid); +extern const char *names_protocol(u_int8_t classid, u_int8_t subclassid, + u_int8_t protocolid); +extern int names_init(char *n); +extern void names_free(void); + +#endif /* _NAMES_H */ diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.c b/drivers/staging/usbip/userspace/libsrc/usbip_common.c new file mode 100644 index 00000000..17e08e02 --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#include "usbip_common.h" +#include "names.h" + +#undef PROGNAME +#define PROGNAME "libusbip" + +int usbip_use_syslog; +int usbip_use_stderr; +int usbip_use_debug; + +struct speed_string { + int num; + char *speed; + char *desc; +}; + +static const struct speed_string speed_strings[] = { + { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"}, + { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" }, + { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" }, + { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, + { 0, NULL, NULL } +}; + +struct portst_string { + int num; + char *desc; +}; + +static struct portst_string portst_strings[] = { + { SDEV_ST_AVAILABLE, "Device Available" }, + { SDEV_ST_USED, "Device in Use" }, + { SDEV_ST_ERROR, "Device Error"}, + { VDEV_ST_NULL, "Port Available"}, + { VDEV_ST_NOTASSIGNED, "Port Initializing"}, + { VDEV_ST_USED, "Port in Use"}, + { VDEV_ST_ERROR, "Port Error"}, + { 0, NULL} +}; + +const char *usbip_status_string(int32_t status) +{ + for (int i = 0; portst_strings[i].desc != NULL; i++) + if (portst_strings[i].num == status) + return portst_strings[i].desc; + + return "Unknown Status"; +} + +const char *usbip_speed_string(int num) +{ + for (int i = 0; speed_strings[i].speed != NULL; i++) + if (speed_strings[i].num == num) + return speed_strings[i].desc; + + return "Unknown Speed"; +} + + +#define DBG_UDEV_INTEGER(name)\ + dbg("%-20s = %x", to_string(name), (int) udev->name) + +#define DBG_UINF_INTEGER(name)\ + dbg("%-20s = %x", to_string(name), (int) uinf->name) + +void dump_usb_interface(struct usbip_usb_interface *uinf) +{ + char buff[100]; + usbip_names_get_class(buff, sizeof(buff), + uinf->bInterfaceClass, + uinf->bInterfaceSubClass, + uinf->bInterfaceProtocol); + dbg("%-20s = %s", "Interface(C/SC/P)", buff); +} + +void dump_usb_device(struct usbip_usb_device *udev) +{ + char buff[100]; + + + dbg("%-20s = %s", "path", udev->path); + dbg("%-20s = %s", "busid", udev->busid); + + usbip_names_get_class(buff, sizeof(buff), + udev->bDeviceClass, + udev->bDeviceSubClass, + udev->bDeviceProtocol); + dbg("%-20s = %s", "Device(C/SC/P)", buff); + + DBG_UDEV_INTEGER(bcdDevice); + + usbip_names_get_product(buff, sizeof(buff), + udev->idVendor, + udev->idProduct); + dbg("%-20s = %s", "Vendor/Product", buff); + + DBG_UDEV_INTEGER(bNumConfigurations); + DBG_UDEV_INTEGER(bNumInterfaces); + + dbg("%-20s = %s", "speed", + usbip_speed_string(udev->speed)); + + DBG_UDEV_INTEGER(busnum); + DBG_UDEV_INTEGER(devnum); +} + + +int read_attr_value(struct sysfs_device *dev, const char *name, + const char *format) +{ + char attrpath[SYSFS_PATH_MAX]; + struct sysfs_attribute *attr; + int num = 0; + int ret; + + snprintf(attrpath, sizeof(attrpath), "%s/%s", dev->path, name); + + attr = sysfs_open_attribute(attrpath); + if (!attr) { + dbg("sysfs_open_attribute failed: %s", attrpath); + return 0; + } + + ret = sysfs_read_attribute(attr); + if (ret < 0) { + dbg("sysfs_read_attribute failed"); + goto err; + } + + ret = sscanf(attr->value, format, &num); + if (ret < 1) { + dbg("sscanf failed"); + goto err; + } + +err: + sysfs_close_attribute(attr); + + return num; +} + + +int read_attr_speed(struct sysfs_device *dev) +{ + char attrpath[SYSFS_PATH_MAX]; + struct sysfs_attribute *attr; + char speed[100]; + int ret; + + snprintf(attrpath, sizeof(attrpath), "%s/%s", dev->path, "speed"); + + attr = sysfs_open_attribute(attrpath); + if (!attr) { + dbg("sysfs_open_attribute failed: %s", attrpath); + return 0; + } + + ret = sysfs_read_attribute(attr); + if (ret < 0) { + dbg("sysfs_read_attribute failed"); + goto err; + } + + ret = sscanf(attr->value, "%s\n", speed); + if (ret < 1) { + dbg("sscanf failed"); + goto err; + } +err: + sysfs_close_attribute(attr); + + for (int i = 0; speed_strings[i].speed != NULL; i++) { + if (!strcmp(speed, speed_strings[i].speed)) + return speed_strings[i].num; + } + + return USB_SPEED_UNKNOWN; +} + +#define READ_ATTR(object, type, dev, name, format) \ + do { \ + (object)->name = (type) read_attr_value(dev, to_string(name), \ + format); \ + } while (0) + + +int read_usb_device(struct sysfs_device *sdev, struct usbip_usb_device *udev) +{ + uint32_t busnum, devnum; + + READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n"); + READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n"); + READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n"); + + READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n"); + READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n"); + READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n"); + + READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n"); + READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n"); + READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n"); + + READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n"); + udev->speed = read_attr_speed(sdev); + + strncpy(udev->path, sdev->path, SYSFS_PATH_MAX); + strncpy(udev->busid, sdev->name, SYSFS_BUS_ID_SIZE); + + sscanf(sdev->name, "%u-%u", &busnum, &devnum); + udev->busnum = busnum; + + return 0; +} + +int read_usb_interface(struct usbip_usb_device *udev, int i, + struct usbip_usb_interface *uinf) +{ + char busid[SYSFS_BUS_ID_SIZE]; + struct sysfs_device *sif; + + sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i); + + sif = sysfs_open_device("usb", busid); + if (!sif) { + dbg("sysfs_open_device(\"usb\", \"%s\") failed", busid); + return -1; + } + + READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n"); + READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n"); + READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n"); + + sysfs_close_device(sif); + + return 0; +} + +int usbip_names_init(char *f) +{ + return names_init(f); +} + +void usbip_names_free() +{ + names_free(); +} + +void usbip_names_get_product(char *buff, size_t size, uint16_t vendor, + uint16_t product) +{ + const char *prod, *vend; + + prod = names_product(vendor, product); + if (!prod) + prod = "unknown product"; + + + vend = names_vendor(vendor); + if (!vend) + vend = "unknown vendor"; + + snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product); +} + +void usbip_names_get_class(char *buff, size_t size, uint8_t class, + uint8_t subclass, uint8_t protocol) +{ + const char *c, *s, *p; + + if (class == 0 && subclass == 0 && protocol == 0) { + snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol); + return; + } + + p = names_protocol(class, subclass, protocol); + if (!p) + p = "unknown protocol"; + + s = names_subclass(class, subclass); + if (!s) + s = "unknown subclass"; + + c = names_class(class); + if (!c) + c = "unknown class"; + + snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol); +} diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.h b/drivers/staging/usbip/userspace/libsrc/usbip_common.h new file mode 100644 index 00000000..938ad1c3 --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#ifndef __USBIP_COMMON_H +#define __USBIP_COMMON_H + +#include <sysfs/libsysfs.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <syslog.h> +#include <unistd.h> + +#ifndef USBIDS_FILE +#define USBIDS_FILE "/usr/share/hwdata/usb.ids" +#endif + +#ifndef VHCI_STATE_PATH +#define VHCI_STATE_PATH "/var/run/vhci_hcd" +#endif + +/* kernel module names */ +#define USBIP_CORE_MOD_NAME "usbip-core" +#define USBIP_HOST_DRV_NAME "usbip-host" +#define USBIP_VHCI_DRV_NAME "vhci_hcd" + +extern int usbip_use_syslog; +extern int usbip_use_stderr; +extern int usbip_use_debug ; + +#define PROGNAME "usbip" + +#define pr_fmt(fmt) "%s: %s: " fmt "\n", PROGNAME +#define dbg_fmt(fmt) pr_fmt("%s:%d:[%s] " fmt), "debug", \ + __FILE__, __LINE__, __FUNCTION__ + +#define err(fmt, args...) \ + do { \ + if (usbip_use_syslog) { \ + syslog(LOG_ERR, pr_fmt(fmt), "error", ##args); \ + } \ + if (usbip_use_stderr) { \ + fprintf(stderr, pr_fmt(fmt), "error", ##args); \ + } \ + } while (0) + +#define info(fmt, args...) \ + do { \ + if (usbip_use_syslog) { \ + syslog(LOG_INFO, pr_fmt(fmt), "info", ##args); \ + } \ + if (usbip_use_stderr) { \ + fprintf(stderr, pr_fmt(fmt), "info", ##args); \ + } \ + } while (0) + +#define dbg(fmt, args...) \ + do { \ + if (usbip_use_debug) { \ + if (usbip_use_syslog) { \ + syslog(LOG_DEBUG, dbg_fmt(fmt), ##args); \ + } \ + if (usbip_use_stderr) { \ + fprintf(stderr, dbg_fmt(fmt), ##args); \ + } \ + } \ + } while (0) + +#define BUG() \ + do { \ + err("sorry, it's a bug!"); \ + abort(); \ + } while (0) + +enum usb_device_speed { + USB_SPEED_UNKNOWN = 0, /* enumerating */ + USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ + USB_SPEED_HIGH, /* usb 2.0 */ + USB_SPEED_VARIABLE /* wireless (usb 2.5) */ +}; + +/* FIXME: how to sync with drivers/usbip_common.h ? */ +enum usbip_device_status { + /* sdev is available. */ + SDEV_ST_AVAILABLE = 0x01, + /* sdev is now used. */ + SDEV_ST_USED, + /* sdev is unusable because of a fatal error. */ + SDEV_ST_ERROR, + + /* vdev does not connect a remote device. */ + VDEV_ST_NULL, + /* vdev is used, but the USB address is not assigned yet */ + VDEV_ST_NOTASSIGNED, + VDEV_ST_USED, + VDEV_ST_ERROR +}; + +struct usbip_usb_interface { + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t padding; /* alignment */ +} __attribute__((packed)); + +struct usbip_usb_device { + char path[SYSFS_PATH_MAX]; + char busid[SYSFS_BUS_ID_SIZE]; + + uint32_t busnum; + uint32_t devnum; + uint32_t speed; + + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bConfigurationValue; + uint8_t bNumConfigurations; + uint8_t bNumInterfaces; +} __attribute__((packed)); + +#define to_string(s) #s + +void dump_usb_interface(struct usbip_usb_interface *); +void dump_usb_device(struct usbip_usb_device *); +int read_usb_device(struct sysfs_device *sdev, struct usbip_usb_device *udev); +int read_attr_value(struct sysfs_device *dev, const char *name, + const char *format); +int read_usb_interface(struct usbip_usb_device *udev, int i, + struct usbip_usb_interface *uinf); + +const char *usbip_speed_string(int num); +const char *usbip_status_string(int32_t status); + +int usbip_names_init(char *); +void usbip_names_free(void); +void usbip_names_get_product(char *buff, size_t size, uint16_t vendor, + uint16_t product); +void usbip_names_get_class(char *buff, size_t size, uint8_t class, + uint8_t subclass, uint8_t protocol); + +#endif /* __USBIP_COMMON_H */ diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c new file mode 100644 index 00000000..71a449cf --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <unistd.h> + +#include "usbip_common.h" +#include "usbip_host_driver.h" + +#undef PROGNAME +#define PROGNAME "libusbip" + +struct usbip_host_driver *host_driver; + +#define SYSFS_OPEN_RETRIES 100 + +/* only the first interface value is true! */ +static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) +{ + char attrpath[SYSFS_PATH_MAX]; + struct sysfs_attribute *attr; + int value = 0; + int rc; + struct stat s; + int retries = SYSFS_OPEN_RETRIES; + + /* This access is racy! + * + * Just after detach, our driver removes the sysfs + * files and recreates them. + * + * We may try and fail to open the usbip_status of + * an exported device in the (short) window where + * it has been removed and not yet recreated. + * + * This is a bug in the interface. Nothing we can do + * except work around it here by polling for the sysfs + * usbip_status to reappear. + */ + + snprintf(attrpath, SYSFS_PATH_MAX, "%s/%s:%d.%d/usbip_status", + udev->path, udev->busid, udev->bConfigurationValue, 0); + + while (retries > 0) { + if (stat(attrpath, &s) == 0) + break; + + if (errno != ENOENT) { + dbg("stat failed: %s", attrpath); + return -1; + } + + usleep(10000); /* 10ms */ + retries--; + } + + if (retries == 0) + dbg("usbip_status not ready after %d retries", + SYSFS_OPEN_RETRIES); + else if (retries < SYSFS_OPEN_RETRIES) + dbg("warning: usbip_status ready after %d retries", + SYSFS_OPEN_RETRIES - retries); + + attr = sysfs_open_attribute(attrpath); + if (!attr) { + dbg("sysfs_open_attribute failed: %s", attrpath); + return -1; + } + + rc = sysfs_read_attribute(attr); + if (rc) { + dbg("sysfs_read_attribute failed: %s", attrpath); + sysfs_close_attribute(attr); + return -1; + } + + value = atoi(attr->value); + + sysfs_close_attribute(attr); + + return value; +} + +static struct usbip_exported_device *usbip_exported_device_new(char *sdevpath) +{ + struct usbip_exported_device *edev = NULL; + size_t size; + int i; + + edev = calloc(1, sizeof(*edev)); + if (!edev) { + dbg("calloc failed"); + return NULL; + } + + edev->sudev = sysfs_open_device_path(sdevpath); + if (!edev->sudev) { + dbg("sysfs_open_device_path failed: %s", sdevpath); + goto err; + } + + read_usb_device(edev->sudev, &edev->udev); + + edev->status = read_attr_usbip_status(&edev->udev); + if (edev->status < 0) + goto err; + + /* reallocate buffer to include usb interface data */ + size = sizeof(*edev) + edev->udev.bNumInterfaces * + sizeof(struct usbip_usb_interface); + + edev = realloc(edev, size); + if (!edev) { + dbg("realloc failed"); + goto err; + } + + for (i = 0; i < edev->udev.bNumInterfaces; i++) + read_usb_interface(&edev->udev, i, &edev->uinf[i]); + + return edev; +err: + if (edev && edev->sudev) + sysfs_close_device(edev->sudev); + if (edev) + free(edev); + + return NULL; +} + +static int check_new(struct dlist *dlist, struct sysfs_device *target) +{ + struct sysfs_device *dev; + + dlist_for_each_data(dlist, dev, struct sysfs_device) { + if (!strncmp(dev->bus_id, target->bus_id, SYSFS_BUS_ID_SIZE)) + /* device found and is not new */ + return 0; + } + return 1; +} + +static void delete_nothing(void *unused_data) +{ + /* + * NOTE: Do not delete anything, but the container will be deleted. + */ + (void) unused_data; +} + +static int refresh_exported_devices(void) +{ + /* sysfs_device of usb_interface */ + struct sysfs_device *suintf; + struct dlist *suintf_list; + /* sysfs_device of usb_device */ + struct sysfs_device *sudev; + struct dlist *sudev_list; + struct usbip_exported_device *edev; + + sudev_list = dlist_new_with_delete(sizeof(struct sysfs_device), + delete_nothing); + + suintf_list = sysfs_get_driver_devices(host_driver->sysfs_driver); + if (!suintf_list) { + /* + * Not an error condition. There are simply no devices bound to + * the driver yet. + */ + dbg("bind " USBIP_HOST_DRV_NAME ".ko to a usb device to be " + "exportable!"); + return 0; + } + + /* collect unique USB devices (not interfaces) */ + dlist_for_each_data(suintf_list, suintf, struct sysfs_device) { + /* get usb device of this usb interface */ + sudev = sysfs_get_device_parent(suintf); + if (!sudev) { + dbg("sysfs_get_device_parent failed: %s", suintf->name); + continue; + } + + if (check_new(sudev_list, sudev)) { + /* insert item at head of list */ + dlist_unshift(sudev_list, sudev); + } + } + + dlist_for_each_data(sudev_list, sudev, struct sysfs_device) { + edev = usbip_exported_device_new(sudev->path); + if (!edev) { + dbg("usbip_exported_device_new failed"); + continue; + } + + dlist_unshift(host_driver->edev_list, edev); + host_driver->ndevs++; + } + + dlist_destroy(sudev_list); + + return 0; +} + +static struct sysfs_driver *open_sysfs_host_driver(void) +{ + char bus_type[] = "usb"; + char sysfs_mntpath[SYSFS_PATH_MAX]; + char host_drv_path[SYSFS_PATH_MAX]; + struct sysfs_driver *host_drv; + int rc; + + rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX); + if (rc < 0) { + dbg("sysfs_get_mnt_path failed"); + return NULL; + } + + snprintf(host_drv_path, SYSFS_PATH_MAX, "%s/%s/%s/%s/%s", + sysfs_mntpath, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME, + USBIP_HOST_DRV_NAME); + + host_drv = sysfs_open_driver_path(host_drv_path); + if (!host_drv) { + dbg("sysfs_open_driver_path failed"); + return NULL; + } + + return host_drv; +} + +static void usbip_exported_device_delete(void *dev) +{ + struct usbip_exported_device *edev = dev; + sysfs_close_device(edev->sudev); + free(dev); +} + +int usbip_host_driver_open(void) +{ + int rc; + + host_driver = calloc(1, sizeof(*host_driver)); + if (!host_driver) { + dbg("calloc failed"); + return -1; + } + + host_driver->ndevs = 0; + host_driver->edev_list = + dlist_new_with_delete(sizeof(struct usbip_exported_device), + usbip_exported_device_delete); + if (!host_driver->edev_list) { + dbg("dlist_new_with_delete failed"); + goto err_free_host_driver; + } + + host_driver->sysfs_driver = open_sysfs_host_driver(); + if (!host_driver->sysfs_driver) + goto err_destroy_edev_list; + + rc = refresh_exported_devices(); + if (rc < 0) + goto err_close_sysfs_driver; + + return 0; + +err_close_sysfs_driver: + sysfs_close_driver(host_driver->sysfs_driver); +err_destroy_edev_list: + dlist_destroy(host_driver->edev_list); +err_free_host_driver: + free(host_driver); + host_driver = NULL; + + return -1; +} + +void usbip_host_driver_close(void) +{ + if (!host_driver) + return; + + if (host_driver->edev_list) + dlist_destroy(host_driver->edev_list); + if (host_driver->sysfs_driver) + sysfs_close_driver(host_driver->sysfs_driver); + + free(host_driver); + host_driver = NULL; +} + +int usbip_host_refresh_device_list(void) +{ + int rc; + + if (host_driver->edev_list) + dlist_destroy(host_driver->edev_list); + + host_driver->ndevs = 0; + host_driver->edev_list = + dlist_new_with_delete(sizeof(struct usbip_exported_device), + usbip_exported_device_delete); + if (!host_driver->edev_list) { + dbg("dlist_new_with_delete failed"); + return -1; + } + + rc = refresh_exported_devices(); + if (rc < 0) + return -1; + + return 0; +} + +int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd) +{ + char attr_name[] = "usbip_sockfd"; + char attr_path[SYSFS_PATH_MAX]; + struct sysfs_attribute *attr; + char sockfd_buff[30]; + int ret; + + if (edev->status != SDEV_ST_AVAILABLE) { + dbg("device not available: %s", edev->udev.busid); + switch (edev->status) { + case SDEV_ST_ERROR: + dbg("status SDEV_ST_ERROR"); + break; + case SDEV_ST_USED: + dbg("status SDEV_ST_USED"); + break; + default: + dbg("status unknown: 0x%x", edev->status); + } + return -1; + } + + /* only the first interface is true */ + snprintf(attr_path, sizeof(attr_path), "%s/%s:%d.%d/%s", + edev->udev.path, edev->udev.busid, + edev->udev.bConfigurationValue, 0, attr_name); + + attr = sysfs_open_attribute(attr_path); + if (!attr) { + dbg("sysfs_open_attribute failed: %s", attr_path); + return -1; + } + + snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); + dbg("write: %s", sockfd_buff); + + ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff)); + if (ret < 0) { + dbg("sysfs_write_attribute failed: sockfd %s to %s", + sockfd_buff, attr_path); + goto err_write_sockfd; + } + + dbg("connect: %s", edev->udev.busid); + +err_write_sockfd: + sysfs_close_attribute(attr); + + return ret; +} + +struct usbip_exported_device *usbip_host_get_device(int num) +{ + struct usbip_exported_device *edev; + struct dlist *dlist = host_driver->edev_list; + int cnt = 0; + + dlist_for_each_data(dlist, edev, struct usbip_exported_device) { + if (num == cnt) + return edev; + else + cnt++; + } + + return NULL; +} diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h new file mode 100644 index 00000000..34fd14cb --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __USBIP_HOST_DRIVER_H +#define __USBIP_HOST_DRIVER_H + +#include <stdint.h> +#include "usbip_common.h" + +struct usbip_host_driver { + int ndevs; + struct sysfs_driver *sysfs_driver; + /* list of exported device */ + struct dlist *edev_list; +}; + +struct usbip_exported_device { + struct sysfs_device *sudev; + int32_t status; + struct usbip_usb_device udev; + struct usbip_usb_interface uinf[]; +}; + +extern struct usbip_host_driver *host_driver; + +int usbip_host_driver_open(void); +void usbip_host_driver_close(void); + +int usbip_host_refresh_device_list(void); +int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd); +struct usbip_exported_device *usbip_host_get_device(int num); + +#endif /* __USBIP_HOST_DRIVER_H */ diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c new file mode 100644 index 00000000..25e62e9f --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#include "usbip_common.h" +#include "vhci_driver.h" + +#undef PROGNAME +#define PROGNAME "libusbip" + +struct usbip_vhci_driver *vhci_driver; + +static struct usbip_imported_device * +imported_device_init(struct usbip_imported_device *idev, char *busid) +{ + struct sysfs_device *sudev; + + sudev = sysfs_open_device("usb", busid); + if (!sudev) { + dbg("sysfs_open_device failed: %s", busid); + goto err; + } + read_usb_device(sudev, &idev->udev); + sysfs_close_device(sudev); + + /* add class devices of this imported device */ + struct usbip_class_device *cdev; + dlist_for_each_data(vhci_driver->cdev_list, cdev, + struct usbip_class_device) { + if (!strncmp(cdev->dev_path, idev->udev.path, + strlen(idev->udev.path))) { + struct usbip_class_device *new_cdev; + /* + * alloc and copy because dlist is linked + * from only one list + */ + new_cdev = calloc(1, sizeof(*new_cdev)); + if (!new_cdev) + goto err; + + memcpy(new_cdev, cdev, sizeof(*new_cdev)); + dlist_unshift(idev->cdev_list, (void *) new_cdev); + } + } + + return idev; + +err: + return NULL; +} + + + +static int parse_status(char *value) +{ + int ret = 0; + char *c; + + + for (int i = 0; i < vhci_driver->nports; i++) + memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i])); + + + /* skip a header line */ + c = strchr(value, '\n'); + if (!c) + return -1; + c++; + + while (*c != '\0') { + int port, status, speed, devid; + unsigned long socket; + char lbusid[SYSFS_BUS_ID_SIZE]; + + ret = sscanf(c, "%d %d %d %x %lx %s\n", + &port, &status, &speed, + &devid, &socket, lbusid); + + if (ret < 5) { + dbg("sscanf failed: %d", ret); + BUG(); + } + + dbg("port %d status %d speed %d devid %x", + port, status, speed, devid); + dbg("socket %lx lbusid %s", socket, lbusid); + + + /* if a device is connected, look at it */ + { + struct usbip_imported_device *idev = &vhci_driver->idev[port]; + + idev->port = port; + idev->status = status; + + idev->devid = devid; + + idev->busnum = (devid >> 16); + idev->devnum = (devid & 0x0000ffff); + + idev->cdev_list = dlist_new(sizeof(struct usbip_class_device)); + if (!idev->cdev_list) { + dbg("dlist_new failed"); + return -1; + } + + if (idev->status != VDEV_ST_NULL + && idev->status != VDEV_ST_NOTASSIGNED) { + idev = imported_device_init(idev, lbusid); + if (!idev) { + dbg("imported_device_init failed"); + return -1; + } + } + } + + + /* go to the next line */ + c = strchr(c, '\n'); + if (!c) + break; + c++; + } + + dbg("exit"); + + return 0; +} + + +static int check_usbip_device(struct sysfs_class_device *cdev) +{ + /* /sys/class/video4linux/video0/device */ + char class_path[SYSFS_PATH_MAX]; + /* /sys/devices/platform/vhci_hcd/usb6/6-1:1.1 */ + char dev_path[SYSFS_PATH_MAX]; + int ret; + struct usbip_class_device *usbip_cdev; + + snprintf(class_path, sizeof(class_path), "%s/device", cdev->path); + + ret = sysfs_get_link(class_path, dev_path, sizeof(dev_path)); + if (ret == 0) { + if (!strncmp(dev_path, vhci_driver->hc_device->path, + strlen(vhci_driver->hc_device->path))) { + /* found usbip device */ + usbip_cdev = calloc(1, sizeof(*usbip_cdev)); + if (!usbip_cdev) { + dbg("calloc failed"); + return -1; + } + dlist_unshift(vhci_driver->cdev_list, usbip_cdev); + strncpy(usbip_cdev->class_path, class_path, + sizeof(usbip_cdev->class_path)); + strncpy(usbip_cdev->dev_path, dev_path, + sizeof(usbip_cdev->dev_path)); + dbg("found: %s %s", class_path, dev_path); + } + } + + return 0; +} + + +static int search_class_for_usbip_device(char *cname) +{ + struct sysfs_class *class; + struct dlist *cdev_list; + struct sysfs_class_device *cdev; + int ret = 0; + + class = sysfs_open_class(cname); + if (!class) { + dbg("sysfs_open_class failed"); + return -1; + } + + dbg("class: %s", class->name); + + cdev_list = sysfs_get_class_devices(class); + if (!cdev_list) + /* nothing */ + goto out; + + dlist_for_each_data(cdev_list, cdev, struct sysfs_class_device) { + dbg("cdev: %s", cdev->name); + ret = check_usbip_device(cdev); + if (ret < 0) + goto out; + } + +out: + sysfs_close_class(class); + + return ret; +} + + +static int refresh_class_device_list(void) +{ + int ret; + struct dlist *cname_list; + char *cname; + char sysfs_mntpath[SYSFS_PATH_MAX]; + char class_path[SYSFS_PATH_MAX]; + + ret = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX); + if (ret < 0) { + dbg("sysfs_get_mnt_path failed"); + return -1; + } + + snprintf(class_path, sizeof(class_path), "%s/%s", sysfs_mntpath, + SYSFS_CLASS_NAME); + + /* search under /sys/class */ + cname_list = sysfs_open_directory_list(class_path); + if (!cname_list) { + dbg("sysfs_open_directory failed"); + return -1; + } + + dlist_for_each_data(cname_list, cname, char) { + ret = search_class_for_usbip_device(cname); + if (ret < 0) { + sysfs_close_list(cname_list); + return -1; + } + } + + sysfs_close_list(cname_list); + + /* seach under /sys/block */ + ret = search_class_for_usbip_device(SYSFS_BLOCK_NAME); + if (ret < 0) + return -1; + + return 0; +} + + +static int refresh_imported_device_list(void) +{ + struct sysfs_attribute *attr_status; + + + attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status"); + if (!attr_status) { + dbg("sysfs_get_device_attr(\"status\") failed: %s", + vhci_driver->hc_device->name); + return -1; + } + + dbg("name: %s path: %s len: %d method: %d value: %s", + attr_status->name, attr_status->path, attr_status->len, + attr_status->method, attr_status->value); + + return parse_status(attr_status->value); +} + +static int get_nports(void) +{ + char *c; + int nports = 0; + struct sysfs_attribute *attr_status; + + attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status"); + if (!attr_status) { + dbg("sysfs_get_device_attr(\"status\") failed: %s", + vhci_driver->hc_device->name); + return -1; + } + + dbg("name: %s path: %s len: %d method: %d value: %s", + attr_status->name, attr_status->path, attr_status->len, + attr_status->method, attr_status->value); + + /* skip a header line */ + c = strchr(attr_status->value, '\n'); + if (!c) + return 0; + c++; + + while (*c != '\0') { + /* go to the next line */ + c = strchr(c, '\n'); + if (!c) + return nports; + c++; + nports += 1; + } + + return nports; +} + +static int get_hc_busid(char *sysfs_mntpath, char *hc_busid) +{ + struct sysfs_driver *sdriver; + char sdriver_path[SYSFS_PATH_MAX]; + + struct sysfs_device *hc_dev; + struct dlist *hc_devs; + + int found = 0; + + snprintf(sdriver_path, SYSFS_PATH_MAX, "%s/%s/%s/%s/%s", sysfs_mntpath, + SYSFS_BUS_NAME, USBIP_VHCI_BUS_TYPE, SYSFS_DRIVERS_NAME, + USBIP_VHCI_DRV_NAME); + + sdriver = sysfs_open_driver_path(sdriver_path); + if (!sdriver) { + dbg("sysfs_open_driver_path failed: %s", sdriver_path); + dbg("make sure " USBIP_CORE_MOD_NAME ".ko and " + USBIP_VHCI_DRV_NAME ".ko are loaded!"); + return -1; + } + + hc_devs = sysfs_get_driver_devices(sdriver); + if (!hc_devs) { + dbg("sysfs_get_driver failed"); + goto err; + } + + /* assume only one vhci_hcd */ + dlist_for_each_data(hc_devs, hc_dev, struct sysfs_device) { + strncpy(hc_busid, hc_dev->bus_id, SYSFS_BUS_ID_SIZE); + found = 1; + } + +err: + sysfs_close_driver(sdriver); + + if (found) + return 0; + + dbg("%s not found", hc_busid); + return -1; +} + + +/* ---------------------------------------------------------------------- */ + +int usbip_vhci_driver_open(void) +{ + int ret; + char hc_busid[SYSFS_BUS_ID_SIZE]; + + vhci_driver = (struct usbip_vhci_driver *) calloc(1, sizeof(*vhci_driver)); + if (!vhci_driver) { + dbg("calloc failed"); + return -1; + } + + ret = sysfs_get_mnt_path(vhci_driver->sysfs_mntpath, SYSFS_PATH_MAX); + if (ret < 0) { + dbg("sysfs_get_mnt_path failed"); + goto err; + } + + ret = get_hc_busid(vhci_driver->sysfs_mntpath, hc_busid); + if (ret < 0) + goto err; + + /* will be freed in usbip_driver_close() */ + vhci_driver->hc_device = sysfs_open_device(USBIP_VHCI_BUS_TYPE, + hc_busid); + if (!vhci_driver->hc_device) { + dbg("sysfs_open_device failed"); + goto err; + } + + vhci_driver->nports = get_nports(); + + dbg("available ports: %d", vhci_driver->nports); + + vhci_driver->cdev_list = dlist_new(sizeof(struct usbip_class_device)); + if (!vhci_driver->cdev_list) + goto err; + + if (refresh_class_device_list()) + goto err; + + if (refresh_imported_device_list()) + goto err; + + + return 0; + + +err: + if (vhci_driver->cdev_list) + dlist_destroy(vhci_driver->cdev_list); + if (vhci_driver->hc_device) + sysfs_close_device(vhci_driver->hc_device); + if (vhci_driver) + free(vhci_driver); + + vhci_driver = NULL; + return -1; +} + + +void usbip_vhci_driver_close() +{ + if (!vhci_driver) + return; + + if (vhci_driver->cdev_list) + dlist_destroy(vhci_driver->cdev_list); + + for (int i = 0; i < vhci_driver->nports; i++) { + if (vhci_driver->idev[i].cdev_list) + dlist_destroy(vhci_driver->idev[i].cdev_list); + } + + if (vhci_driver->hc_device) + sysfs_close_device(vhci_driver->hc_device); + free(vhci_driver); + + vhci_driver = NULL; +} + + +int usbip_vhci_refresh_device_list(void) +{ + if (vhci_driver->cdev_list) + dlist_destroy(vhci_driver->cdev_list); + + + for (int i = 0; i < vhci_driver->nports; i++) { + if (vhci_driver->idev[i].cdev_list) + dlist_destroy(vhci_driver->idev[i].cdev_list); + } + + vhci_driver->cdev_list = dlist_new(sizeof(struct usbip_class_device)); + if (!vhci_driver->cdev_list) + goto err; + + if (refresh_class_device_list()) + goto err; + + if (refresh_imported_device_list()) + goto err; + + return 0; +err: + if (vhci_driver->cdev_list) + dlist_destroy(vhci_driver->cdev_list); + + for (int i = 0; i < vhci_driver->nports; i++) { + if (vhci_driver->idev[i].cdev_list) + dlist_destroy(vhci_driver->idev[i].cdev_list); + } + + dbg("failed to refresh device list"); + return -1; +} + + +int usbip_vhci_get_free_port(void) +{ + for (int i = 0; i < vhci_driver->nports; i++) { + if (vhci_driver->idev[i].status == VDEV_ST_NULL) + return i; + } + + return -1; +} + +int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, + uint32_t speed) { + struct sysfs_attribute *attr_attach; + char buff[200]; /* what size should be ? */ + int ret; + + attr_attach = sysfs_get_device_attr(vhci_driver->hc_device, "attach"); + if (!attr_attach) { + dbg("sysfs_get_device_attr(\"attach\") failed: %s", + vhci_driver->hc_device->name); + return -1; + } + + snprintf(buff, sizeof(buff), "%u %u %u %u", + port, sockfd, devid, speed); + dbg("writing: %s", buff); + + ret = sysfs_write_attribute(attr_attach, buff, strlen(buff)); + if (ret < 0) { + dbg("sysfs_write_attribute failed"); + return -1; + } + + dbg("attached port: %d", port); + + return 0; +} + +static unsigned long get_devid(uint8_t busnum, uint8_t devnum) +{ + return (busnum << 16) | devnum; +} + +/* will be removed */ +int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum, + uint8_t devnum, uint32_t speed) +{ + int devid = get_devid(busnum, devnum); + + return usbip_vhci_attach_device2(port, sockfd, devid, speed); +} + +int usbip_vhci_detach_device(uint8_t port) +{ + struct sysfs_attribute *attr_detach; + char buff[200]; /* what size should be ? */ + int ret; + + attr_detach = sysfs_get_device_attr(vhci_driver->hc_device, "detach"); + if (!attr_detach) { + dbg("sysfs_get_device_attr(\"detach\") failed: %s", + vhci_driver->hc_device->name); + return -1; + } + + snprintf(buff, sizeof(buff), "%u", port); + dbg("writing: %s", buff); + + ret = sysfs_write_attribute(attr_detach, buff, strlen(buff)); + if (ret < 0) { + dbg("sysfs_write_attribute failed"); + return -1; + } + + dbg("detached port: %d", port); + + return 0; +} diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.h b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h new file mode 100644 index 00000000..89949aa7 --- /dev/null +++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#ifndef __VHCI_DRIVER_H +#define __VHCI_DRIVER_H + +#include <sysfs/libsysfs.h> +#include <stdint.h> + +#include "usbip_common.h" + +#define USBIP_VHCI_BUS_TYPE "platform" +#define MAXNPORT 128 + +struct usbip_class_device { + char class_path[SYSFS_PATH_MAX]; + char dev_path[SYSFS_PATH_MAX]; +}; + +struct usbip_imported_device { + uint8_t port; + uint32_t status; + + uint32_t devid; + + uint8_t busnum; + uint8_t devnum; + + /* usbip_class_device list */ + struct dlist *cdev_list; + struct usbip_usb_device udev; +}; + +struct usbip_vhci_driver { + char sysfs_mntpath[SYSFS_PATH_MAX]; + + /* /sys/devices/platform/vhci_hcd */ + struct sysfs_device *hc_device; + + /* usbip_class_device list */ + struct dlist *cdev_list; + + int nports; + struct usbip_imported_device idev[MAXNPORT]; +}; + + +extern struct usbip_vhci_driver *vhci_driver; + +int usbip_vhci_driver_open(void); +void usbip_vhci_driver_close(void); + +int usbip_vhci_refresh_device_list(void); + + +int usbip_vhci_get_free_port(void); +int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, + uint32_t speed); + +/* will be removed */ +int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum, + uint8_t devnum, uint32_t speed); + +int usbip_vhci_detach_device(uint8_t port); + +#endif /* __VHCI_DRIVER_H */ |
