diff options
Diffstat (limited to 'tools/usb/usbip/libsrc')
-rw-r--r-- | tools/usb/usbip/libsrc/Makefile.am | 11 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/list.h | 137 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/names.c | 486 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/names.h | 26 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/sysfs_utils.c | 32 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/sysfs_utils.h | 9 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_common.c | 318 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_common.h | 152 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_device_driver.c | 158 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_device_driver.h | 22 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_host_common.c | 282 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_host_common.h | 92 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_host_driver.c | 53 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_host_driver.h | 20 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/vhci_driver.c | 467 | ||||
-rw-r--r-- | tools/usb/usbip/libsrc/vhci_driver.h | 67 |
16 files changed, 2332 insertions, 0 deletions
diff --git a/tools/usb/usbip/libsrc/Makefile.am b/tools/usb/usbip/libsrc/Makefile.am new file mode 100644 index 000000000..dabd2c91d --- /dev/null +++ b/tools/usb/usbip/libsrc/Makefile.am @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +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_device_driver.c usbip_device_driver.h \ + usbip_common.c usbip_common.h usbip_host_common.h \ + usbip_host_common.c vhci_driver.c vhci_driver.h \ + sysfs_utils.c sysfs_utils.h diff --git a/tools/usb/usbip/libsrc/list.h b/tools/usb/usbip/libsrc/list.h new file mode 100644 index 000000000..a941671e4 --- /dev/null +++ b/tools/usb/usbip/libsrc/list.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LIST_H +#define _LIST_H + +/* Stripped down implementation of linked list taken + * from the Linux Kernel. + */ + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +#define POISON_POINTER_DELTA 0 +#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) +#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA) + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#endif diff --git a/tools/usb/usbip/libsrc/names.c b/tools/usb/usbip/libsrc/names.c new file mode 100644 index 000000000..aba7f4188 --- /dev/null +++ b/tools/usb/usbip/libsrc/names.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * names.c -- USB name database manipulation routines + * + * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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) + return NULL; + + p->mem = calloc(1, size); + if (!p->mem) { + free(p); + 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/tools/usb/usbip/libsrc/names.h b/tools/usb/usbip/libsrc/names.h new file mode 100644 index 000000000..b39958230 --- /dev/null +++ b/tools/usb/usbip/libsrc/names.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * names.h -- USB name database manipulation routines + * + * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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/tools/usb/usbip/libsrc/sysfs_utils.c b/tools/usb/usbip/libsrc/sysfs_utils.c new file mode 100644 index 000000000..14d5e67d3 --- /dev/null +++ b/tools/usb/usbip/libsrc/sysfs_utils.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include "sysfs_utils.h" +#include "usbip_common.h" + +int write_sysfs_attribute(const char *attr_path, const char *new_value, + size_t len) +{ + int fd; + int length; + + fd = open(attr_path, O_WRONLY); + if (fd < 0) { + dbg("error opening attribute %s", attr_path); + return -1; + } + + length = write(fd, new_value, len); + if (length < 0) { + dbg("error writing to attribute %s", attr_path); + close(fd); + return -1; + } + + close(fd); + + return 0; +} diff --git a/tools/usb/usbip/libsrc/sysfs_utils.h b/tools/usb/usbip/libsrc/sysfs_utils.h new file mode 100644 index 000000000..0cd5f17e7 --- /dev/null +++ b/tools/usb/usbip/libsrc/sysfs_utils.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __SYSFS_UTILS_H +#define __SYSFS_UTILS_H + +int write_sysfs_attribute(const char *attr_path, const char *new_value, + size_t len); + +#endif diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c new file mode 100644 index 000000000..b8d7d4805 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_common.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#include <libudev.h> +#include "usbip_common.h" +#include "names.h" + +#undef PROGNAME +#define PROGNAME "libusbip" + +int usbip_use_syslog; +int usbip_use_stderr; +int usbip_use_debug; + +extern struct udev *udev_context; + +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)" }, + { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, + { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, + { 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"; +} + +struct op_common_status_string { + int num; + char *desc; +}; + +static struct op_common_status_string op_common_status_strings[] = { + { ST_OK, "Request Completed Successfully" }, + { ST_NA, "Request Failed" }, + { ST_DEV_BUSY, "Device busy (exported)" }, + { ST_DEV_ERR, "Device in error state" }, + { ST_NODEV, "Device not found" }, + { ST_ERROR, "Unexpected response" }, + { 0, NULL} +}; + +const char *usbip_op_common_status_string(int status) +{ + for (int i = 0; op_common_status_strings[i].desc != NULL; i++) + if (op_common_status_strings[i].num == status) + return op_common_status_strings[i].desc; + + return "Unknown Op Common Status"; +} + +#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 udev_device *dev, const char *name, + const char *format) +{ + const char *attr; + int num = 0; + int ret; + + attr = udev_device_get_sysattr_value(dev, name); + if (!attr) { + err("udev_device_get_sysattr_value failed"); + goto err; + } + + /* The client chooses the device configuration + * when attaching it so right after being bound + * to usbip-host on the server the device will + * have no configuration. + * Therefore, attributes such as bConfigurationValue + * and bNumInterfaces will not exist and sscanf will + * fail. Check for these cases and don't treat them + * as errors. + */ + + ret = sscanf(attr, format, &num); + if (ret < 1) { + if (strcmp(name, "bConfigurationValue") && + strcmp(name, "bNumInterfaces")) { + err("sscanf failed for attribute %s", name); + goto err; + } + } + +err: + + return num; +} + + +int read_attr_speed(struct udev_device *dev) +{ + const char *speed; + + speed = udev_device_get_sysattr_value(dev, "speed"); + if (!speed) { + err("udev_device_get_sysattr_value failed"); + goto err; + } + + for (int i = 0; speed_strings[i].speed != NULL; i++) { + if (!strcmp(speed, speed_strings[i].speed)) + return speed_strings[i].num; + } + +err: + + 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 udev_device *sdev, struct usbip_usb_device *udev) +{ + uint32_t busnum, devnum; + const char *path, *name; + + 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); + + path = udev_device_get_syspath(sdev); + name = udev_device_get_sysname(sdev); + + strncpy(udev->path, path, SYSFS_PATH_MAX - 1); + udev->path[SYSFS_PATH_MAX - 1] = '\0'; + strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1); + udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; + + sscanf(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]; + int size; + struct udev_device *sif; + + size = snprintf(busid, sizeof(busid), "%s:%d.%d", + udev->busid, udev->bConfigurationValue, i); + if (size < 0 || (unsigned int)size >= sizeof(busid)) { + err("busid length %i >= %lu or < 0", size, + (long unsigned)sizeof(busid)); + return -1; + } + + sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid); + if (!sif) { + err("udev_device_new_from_subsystem_sysname %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"); + + return 0; +} + +int usbip_names_init(char *f) +{ + return names_init(f); +} + +void usbip_names_free(void) +{ + 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/tools/usb/usbip/libsrc/usbip_common.h b/tools/usb/usbip/libsrc/usbip_common.h new file mode 100644 index 000000000..73a367a7f --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_common.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#ifndef __USBIP_COMMON_H +#define __USBIP_COMMON_H + +#include <libudev.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <syslog.h> +#include <unistd.h> +#include <linux/usb/ch9.h> +#include <linux/usbip.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 + +#define VUDC_DEVICE_DESCR_FILE "dev_desc" + +/* kernel module names */ +#define USBIP_CORE_MOD_NAME "usbip-core" +#define USBIP_HOST_DRV_NAME "usbip-host" +#define USBIP_DEVICE_DRV_NAME "usbip-vudc" +#define USBIP_VHCI_DRV_NAME "vhci_hcd" + +/* sysfs constants */ +#define SYSFS_MNT_PATH "/sys" +#define SYSFS_BUS_NAME "bus" +#define SYSFS_BUS_TYPE "usb" +#define SYSFS_DRIVERS_NAME "drivers" + +#define SYSFS_PATH_MAX 256 +#define SYSFS_BUS_ID_SIZE 32 + +/* Defines for op_code status in server/client op_common PDUs */ +#define ST_OK 0x00 +#define ST_NA 0x01 + /* Device requested for import is not available */ +#define ST_DEV_BUSY 0x02 + /* Device requested for import is in error state */ +#define ST_DEV_ERR 0x03 +#define ST_NODEV 0x04 +#define ST_ERROR 0x05 + +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__, __func__ + +#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) + +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 udev_device *sdev, struct usbip_usb_device *udev); +int read_attr_value(struct udev_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); +const char *usbip_op_common_status_string(int 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/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c new file mode 100644 index 000000000..927a151fa --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_device_driver.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2015 Karol Kosik <karo9@interia.eu> + * 2015 Samsung Electronics + * Author: Igor Kotrasinski <i.kotrasinsk@samsung.com> + * + * Based on tools/usb/usbip/libsrc/usbip_host_driver.c, which is: + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + */ + +#include <fcntl.h> +#include <string.h> +#include <linux/usb/ch9.h> + +#include <unistd.h> + +#include "usbip_host_common.h" +#include "usbip_device_driver.h" + +#undef PROGNAME +#define PROGNAME "libusbip" + +#define copy_descr_attr16(dev, descr, attr) \ + ((dev)->attr = le16toh((descr)->attr)) \ + +#define copy_descr_attr(dev, descr, attr) \ + ((dev)->attr = (descr)->attr) \ + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static struct { + enum usb_device_speed speed; + const char *name; +} speed_names[] = { + { + .speed = USB_SPEED_UNKNOWN, + .name = "UNKNOWN", + }, + { + .speed = USB_SPEED_LOW, + .name = "low-speed", + }, + { + .speed = USB_SPEED_FULL, + .name = "full-speed", + }, + { + .speed = USB_SPEED_HIGH, + .name = "high-speed", + }, + { + .speed = USB_SPEED_WIRELESS, + .name = "wireless", + }, + { + .speed = USB_SPEED_SUPER, + .name = "super-speed", + }, +}; + +static +int read_usb_vudc_device(struct udev_device *sdev, struct usbip_usb_device *dev) +{ + const char *path, *name; + char filepath[SYSFS_PATH_MAX]; + struct usb_device_descriptor descr; + unsigned int i; + FILE *fd = NULL; + struct udev_device *plat; + const char *speed; + size_t ret; + + plat = udev_device_get_parent(sdev); + path = udev_device_get_syspath(plat); + snprintf(filepath, SYSFS_PATH_MAX, "%s/%s", + path, VUDC_DEVICE_DESCR_FILE); + fd = fopen(filepath, "r"); + if (!fd) + return -1; + ret = fread((char *) &descr, sizeof(descr), 1, fd); + if (ret != 1) { + err("Cannot read vudc device descr file: %s", strerror(errno)); + goto err; + } + fclose(fd); + + copy_descr_attr(dev, &descr, bDeviceClass); + copy_descr_attr(dev, &descr, bDeviceSubClass); + copy_descr_attr(dev, &descr, bDeviceProtocol); + copy_descr_attr(dev, &descr, bNumConfigurations); + copy_descr_attr16(dev, &descr, idVendor); + copy_descr_attr16(dev, &descr, idProduct); + copy_descr_attr16(dev, &descr, bcdDevice); + + strncpy(dev->path, path, SYSFS_PATH_MAX - 1); + dev->path[SYSFS_PATH_MAX - 1] = '\0'; + + dev->speed = USB_SPEED_UNKNOWN; + speed = udev_device_get_sysattr_value(sdev, "current_speed"); + if (speed) { + for (i = 0; i < ARRAY_SIZE(speed_names); i++) { + if (!strcmp(speed_names[i].name, speed)) { + dev->speed = speed_names[i].speed; + break; + } + } + } + + /* Only used for user output, little sense to output them in general */ + dev->bNumInterfaces = 0; + dev->bConfigurationValue = 0; + dev->busnum = 0; + + name = udev_device_get_sysname(plat); + strncpy(dev->busid, name, SYSFS_BUS_ID_SIZE - 1); + dev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0'; + return 0; +err: + fclose(fd); + return -1; +} + +static int is_my_device(struct udev_device *dev) +{ + const char *driver; + + driver = udev_device_get_property_value(dev, "USB_UDC_NAME"); + return driver != NULL && !strcmp(driver, USBIP_DEVICE_DRV_NAME); +} + +static int usbip_device_driver_open(struct usbip_host_driver *hdriver) +{ + int ret; + + hdriver->ndevs = 0; + INIT_LIST_HEAD(&hdriver->edev_list); + + ret = usbip_generic_driver_open(hdriver); + if (ret) + err("please load " USBIP_CORE_MOD_NAME ".ko and " + USBIP_DEVICE_DRV_NAME ".ko!"); + + return ret; +} + +struct usbip_host_driver device_driver = { + .edev_list = LIST_HEAD_INIT(device_driver.edev_list), + .udev_subsystem = "udc", + .ops = { + .open = usbip_device_driver_open, + .close = usbip_generic_driver_close, + .refresh_device_list = usbip_generic_refresh_device_list, + .get_device = usbip_generic_get_device, + .read_device = read_usb_vudc_device, + .is_my_device = is_my_device, + }, +}; diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.h b/tools/usb/usbip/libsrc/usbip_device_driver.h new file mode 100644 index 000000000..1ce0bbd75 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_device_driver.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 Karol Kosik <karo9@interia.eu> + * 2015 Samsung Electronics + * Author: Igor Kotrasinski <i.kotrasinsk@samsung.com> + * + * Based on tools/usb/usbip/libsrc/usbip_host_driver.c, which is: + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + */ + +#ifndef __USBIP_DEVICE_DRIVER_H +#define __USBIP_DEVICE_DRIVER_H + +#include <stdint.h> +#include "usbip_common.h" +#include "usbip_host_common.h" +#include "list.h" + +extern struct usbip_host_driver device_driver; + +#endif /* __USBIP_DEVICE_DRIVER_H */ diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c new file mode 100644 index 000000000..ca78aa368 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_common.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2015-2016 Samsung Electronics + * Igor Kotrasinski <i.kotrasinsk@samsung.com> + * Krzysztof Opasiak <k.opasiak@samsung.com> + * + * Refactored from usbip_host_driver.c, which is: + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <errno.h> +#include <unistd.h> + +#include <libudev.h> + +#include "usbip_common.h" +#include "usbip_host_common.h" +#include "list.h" +#include "sysfs_utils.h" + +extern struct udev *udev_context; + +static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) +{ + char status_attr_path[SYSFS_PATH_MAX]; + int size; + int fd; + int length; + char status[2] = { 0 }; + int value = 0; + + size = snprintf(status_attr_path, sizeof(status_attr_path), + "%s/usbip_status", udev->path); + if (size < 0 || (unsigned int)size >= sizeof(status_attr_path)) { + err("usbip_status path length %i >= %lu or < 0", size, + (long unsigned)sizeof(status_attr_path)); + return -1; + } + + + fd = open(status_attr_path, O_RDONLY); + if (fd < 0) { + err("error opening attribute %s", status_attr_path); + return -1; + } + + length = read(fd, status, 1); + if (length < 0) { + err("error reading attribute %s", status_attr_path); + close(fd); + return -1; + } + + value = atoi(status); + close(fd); + return value; +} + +static +struct usbip_exported_device *usbip_exported_device_new( + struct usbip_host_driver *hdriver, const char *sdevpath) +{ + struct usbip_exported_device *edev = NULL; + struct usbip_exported_device *edev_old; + size_t size; + int i; + + edev = calloc(1, sizeof(struct usbip_exported_device)); + + edev->sudev = + udev_device_new_from_syspath(udev_context, sdevpath); + if (!edev->sudev) { + err("udev_device_new_from_syspath: %s", sdevpath); + goto err; + } + + if (hdriver->ops.read_device(edev->sudev, &edev->udev) < 0) + goto err; + + edev->status = read_attr_usbip_status(&edev->udev); + if (edev->status < 0) + goto err; + + /* reallocate buffer to include usb interface data */ + size = sizeof(struct usbip_exported_device) + + edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface); + + edev_old = edev; + edev = realloc(edev, size); + if (!edev) { + edev = edev_old; + dbg("realloc failed"); + goto err; + } + + for (i = 0; i < edev->udev.bNumInterfaces; i++) { + /* vudc does not support reading interfaces */ + if (!hdriver->ops.read_interface) + break; + hdriver->ops.read_interface(&edev->udev, i, &edev->uinf[i]); + } + + return edev; +err: + if (edev->sudev) + udev_device_unref(edev->sudev); + if (edev) + free(edev); + + return NULL; +} + +static int refresh_exported_devices(struct usbip_host_driver *hdriver) +{ + struct usbip_exported_device *edev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + struct udev_device *dev; + const char *path; + + enumerate = udev_enumerate_new(udev_context); + udev_enumerate_add_match_subsystem(enumerate, hdriver->udev_subsystem); + udev_enumerate_scan_devices(enumerate); + + devices = udev_enumerate_get_list_entry(enumerate); + + udev_list_entry_foreach(dev_list_entry, devices) { + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev_context, + path); + if (dev == NULL) + continue; + + /* Check whether device uses usbip driver. */ + if (hdriver->ops.is_my_device(dev)) { + edev = usbip_exported_device_new(hdriver, path); + if (!edev) { + dbg("usbip_exported_device_new failed"); + continue; + } + + list_add(&edev->node, &hdriver->edev_list); + hdriver->ndevs++; + } + } + + return 0; +} + +static void usbip_exported_device_destroy(struct list_head *devs) +{ + struct list_head *i, *tmp; + struct usbip_exported_device *edev; + + list_for_each_safe(i, tmp, devs) { + edev = list_entry(i, struct usbip_exported_device, node); + list_del(i); + free(edev); + } +} + +int usbip_generic_driver_open(struct usbip_host_driver *hdriver) +{ + int rc; + + udev_context = udev_new(); + if (!udev_context) { + err("udev_new failed"); + return -1; + } + + rc = refresh_exported_devices(hdriver); + if (rc < 0) + goto err; + return 0; +err: + udev_unref(udev_context); + return -1; +} + +void usbip_generic_driver_close(struct usbip_host_driver *hdriver) +{ + if (!hdriver) + return; + + usbip_exported_device_destroy(&hdriver->edev_list); + + udev_unref(udev_context); +} + +int usbip_generic_refresh_device_list(struct usbip_host_driver *hdriver) +{ + int rc; + + usbip_exported_device_destroy(&hdriver->edev_list); + + hdriver->ndevs = 0; + INIT_LIST_HEAD(&hdriver->edev_list); + + rc = refresh_exported_devices(hdriver); + if (rc < 0) + return -1; + + return 0; +} + +int usbip_export_device(struct usbip_exported_device *edev, int sockfd) +{ + char attr_name[] = "usbip_sockfd"; + char sockfd_attr_path[SYSFS_PATH_MAX]; + int size; + 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"); + ret = ST_DEV_ERR; + break; + case SDEV_ST_USED: + dbg("status SDEV_ST_USED"); + ret = ST_DEV_BUSY; + break; + default: + dbg("status unknown: 0x%x", edev->status); + ret = -1; + } + return ret; + } + + /* only the first interface is true */ + size = snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", + edev->udev.path, attr_name); + if (size < 0 || (unsigned int)size >= sizeof(sockfd_attr_path)) { + err("exported device path length %i >= %lu or < 0", size, + (long unsigned)sizeof(sockfd_attr_path)); + return -1; + } + + size = snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); + if (size < 0 || (unsigned int)size >= sizeof(sockfd_buff)) { + err("socket length %i >= %lu or < 0", size, + (long unsigned)sizeof(sockfd_buff)); + return -1; + } + + ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, + strlen(sockfd_buff)); + if (ret < 0) { + err("write_sysfs_attribute failed: sockfd %s to %s", + sockfd_buff, sockfd_attr_path); + return ret; + } + + info("connect: %s", edev->udev.busid); + + return ret; +} + +struct usbip_exported_device *usbip_generic_get_device( + struct usbip_host_driver *hdriver, int num) +{ + struct list_head *i; + struct usbip_exported_device *edev; + int cnt = 0; + + list_for_each(i, &hdriver->edev_list) { + edev = list_entry(i, struct usbip_exported_device, node); + if (num == cnt) + return edev; + cnt++; + } + + return NULL; +} diff --git a/tools/usb/usbip/libsrc/usbip_host_common.h b/tools/usb/usbip/libsrc/usbip_host_common.h new file mode 100644 index 000000000..f46967c0a --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_common.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015-2016 Samsung Electronics + * Igor Kotrasinski <i.kotrasinsk@samsung.com> + * Krzysztof Opasiak <k.opasiak@samsung.com> + * + * Refactored from usbip_host_driver.c, which is: + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + */ + +#ifndef __USBIP_HOST_COMMON_H +#define __USBIP_HOST_COMMON_H + +#include <stdint.h> +#include <libudev.h> +#include <errno.h> +#include "list.h" +#include "usbip_common.h" +#include "sysfs_utils.h" + +struct usbip_host_driver; + +struct usbip_host_driver_ops { + int (*open)(struct usbip_host_driver *hdriver); + void (*close)(struct usbip_host_driver *hdriver); + int (*refresh_device_list)(struct usbip_host_driver *hdriver); + struct usbip_exported_device * (*get_device)( + struct usbip_host_driver *hdriver, int num); + + int (*read_device)(struct udev_device *sdev, + struct usbip_usb_device *dev); + int (*read_interface)(struct usbip_usb_device *udev, int i, + struct usbip_usb_interface *uinf); + int (*is_my_device)(struct udev_device *udev); +}; + +struct usbip_host_driver { + int ndevs; + /* list of exported device */ + struct list_head edev_list; + const char *udev_subsystem; + struct usbip_host_driver_ops ops; +}; + +struct usbip_exported_device { + struct udev_device *sudev; + int32_t status; + struct usbip_usb_device udev; + struct list_head node; + struct usbip_usb_interface uinf[]; +}; + +/* External API to access the driver */ +static inline int usbip_driver_open(struct usbip_host_driver *hdriver) +{ + if (!hdriver->ops.open) + return -EOPNOTSUPP; + return hdriver->ops.open(hdriver); +} + +static inline void usbip_driver_close(struct usbip_host_driver *hdriver) +{ + if (!hdriver->ops.close) + return; + hdriver->ops.close(hdriver); +} + +static inline int usbip_refresh_device_list(struct usbip_host_driver *hdriver) +{ + if (!hdriver->ops.refresh_device_list) + return -EOPNOTSUPP; + return hdriver->ops.refresh_device_list(hdriver); +} + +static inline struct usbip_exported_device * +usbip_get_device(struct usbip_host_driver *hdriver, int num) +{ + if (!hdriver->ops.get_device) + return NULL; + return hdriver->ops.get_device(hdriver, num); +} + +/* Helper functions for implementing driver backend */ +int usbip_generic_driver_open(struct usbip_host_driver *hdriver); +void usbip_generic_driver_close(struct usbip_host_driver *hdriver); +int usbip_generic_refresh_device_list(struct usbip_host_driver *hdriver); +int usbip_export_device(struct usbip_exported_device *edev, int sockfd); +struct usbip_exported_device *usbip_generic_get_device( + struct usbip_host_driver *hdriver, int num); + +#endif /* __USBIP_HOST_COMMON_H */ diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c new file mode 100644 index 000000000..573e73ec3 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_driver.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + * Copyright (C) 2015-2016 Samsung Electronics + * Igor Kotrasinski <i.kotrasinsk@samsung.com> + * Krzysztof Opasiak <k.opasiak@samsung.com> + */ + +#include <unistd.h> +#include <libudev.h> + +#include "usbip_host_common.h" +#include "usbip_host_driver.h" + +#undef PROGNAME +#define PROGNAME "libusbip" + +static int is_my_device(struct udev_device *dev) +{ + const char *driver; + + driver = udev_device_get_driver(dev); + return driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME); +} + +static int usbip_host_driver_open(struct usbip_host_driver *hdriver) +{ + int ret; + + hdriver->ndevs = 0; + INIT_LIST_HEAD(&hdriver->edev_list); + + ret = usbip_generic_driver_open(hdriver); + if (ret) + err("please load " USBIP_CORE_MOD_NAME ".ko and " + USBIP_HOST_DRV_NAME ".ko!"); + return ret; +} + +struct usbip_host_driver host_driver = { + .edev_list = LIST_HEAD_INIT(host_driver.edev_list), + .udev_subsystem = "usb", + .ops = { + .open = usbip_host_driver_open, + .close = usbip_generic_driver_close, + .refresh_device_list = usbip_generic_refresh_device_list, + .get_device = usbip_generic_get_device, + .read_device = read_usb_device, + .read_interface = read_usb_interface, + .is_my_device = is_my_device, + }, +}; diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.h b/tools/usb/usbip/libsrc/usbip_host_driver.h new file mode 100644 index 000000000..6ba996c5a --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_driver.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> + * 2005-2007 Takahiro Hirofuchi + * Copyright (C) 2015-2016 Samsung Electronics + * Igor Kotrasinski <i.kotrasinsk@samsung.com> + * Krzysztof Opasiak <k.opasiak@samsung.com> + */ + +#ifndef __USBIP_HOST_DRIVER_H +#define __USBIP_HOST_DRIVER_H + +#include <stdint.h> +#include "usbip_common.h" +#include "list.h" +#include "usbip_host_common.h" + +extern struct usbip_host_driver host_driver; + +#endif /* __USBIP_HOST_DRIVER_H */ diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c new file mode 100644 index 000000000..8159fd986 --- /dev/null +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#include "usbip_common.h" +#include "vhci_driver.h" +#include <limits.h> +#include <netdb.h> +#include <libudev.h> +#include <dirent.h> +#include "sysfs_utils.h" + +#undef PROGNAME +#define PROGNAME "libusbip" + +struct usbip_vhci_driver *vhci_driver; +struct udev *udev_context; + +static struct usbip_imported_device * +imported_device_init(struct usbip_imported_device *idev, char *busid) +{ + struct udev_device *sudev; + + sudev = udev_device_new_from_subsystem_sysname(udev_context, + "usb", busid); + if (!sudev) { + dbg("udev_device_new_from_subsystem_sysname failed: %s", busid); + goto err; + } + read_usb_device(sudev, &idev->udev); + udev_device_unref(sudev); + + return idev; + +err: + return NULL; +} + +static int parse_status(const char *value) +{ + int ret = 0; + char *c; + + /* skip a header line */ + c = strchr(value, '\n'); + if (!c) + return -1; + c++; + + while (*c != '\0') { + int port, status, speed, devid; + int sockfd; + char lbusid[SYSFS_BUS_ID_SIZE]; + struct usbip_imported_device *idev; + char hub[3]; + + ret = sscanf(c, "%2s %d %d %d %x %u %31s\n", + hub, &port, &status, &speed, + &devid, &sockfd, lbusid); + + if (ret < 5) { + dbg("sscanf failed: %d", ret); + BUG(); + } + + dbg("hub %s port %d status %d speed %d devid %x", + hub, port, status, speed, devid); + dbg("sockfd %u lbusid %s", sockfd, lbusid); + + /* if a device is connected, look at it */ + idev = &vhci_driver->idev[port]; + memset(idev, 0, sizeof(*idev)); + + if (strncmp("hs", hub, 2) == 0) + idev->hub = HUB_SPEED_HIGH; + else /* strncmp("ss", hub, 2) == 0 */ + idev->hub = HUB_SPEED_SUPER; + + idev->port = port; + idev->status = status; + + idev->devid = devid; + + idev->busnum = (devid >> 16); + idev->devnum = (devid & 0x0000ffff); + + 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; +} + +#define MAX_STATUS_NAME 18 + +static int refresh_imported_device_list(void) +{ + const char *attr_status; + char status[MAX_STATUS_NAME+1] = "status"; + int i, ret; + + for (i = 0; i < vhci_driver->ncontrollers; i++) { + if (i > 0) + snprintf(status, sizeof(status), "status.%d", i); + + attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, + status); + if (!attr_status) { + err("udev_device_get_sysattr_value failed"); + return -1; + } + + dbg("controller %d", i); + + ret = parse_status(attr_status); + if (ret != 0) + return ret; + } + + return 0; +} + +static int get_nports(struct udev_device *hc_device) +{ + const char *attr_nports; + + attr_nports = udev_device_get_sysattr_value(hc_device, "nports"); + if (!attr_nports) { + err("udev_device_get_sysattr_value nports failed"); + return -1; + } + + return (int)strtoul(attr_nports, NULL, 10); +} + +static int vhci_hcd_filter(const struct dirent *dirent) +{ + return !strncmp(dirent->d_name, "vhci_hcd.", 9); +} + +static int get_ncontrollers(void) +{ + struct dirent **namelist; + struct udev_device *platform; + int n; + + platform = udev_device_get_parent(vhci_driver->hc_device); + if (platform == NULL) + return -1; + + n = scandir(udev_device_get_syspath(platform), &namelist, vhci_hcd_filter, NULL); + if (n < 0) + err("scandir failed"); + else { + for (int i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + } + + return n; +} + +/* + * Read the given port's record. + * + * To avoid buffer overflow we will read the entire line and + * validate each part's size. The initial buffer is padded by 4 to + * accommodate the 2 spaces, 1 newline and an additional character + * which is needed to properly validate the 3rd part without it being + * truncated to an acceptable length. + */ +static int read_record(int rhport, char *host, unsigned long host_len, + char *port, unsigned long port_len, char *busid) +{ + int part; + FILE *file; + char path[PATH_MAX+1]; + char *buffer, *start, *end; + char delim[] = {' ', ' ', '\n'}; + int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE}; + size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4; + + buffer = malloc(buffer_len); + if (!buffer) + return -1; + + snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); + + file = fopen(path, "r"); + if (!file) { + err("fopen"); + free(buffer); + return -1; + } + + if (fgets(buffer, buffer_len, file) == NULL) { + err("fgets"); + free(buffer); + fclose(file); + return -1; + } + fclose(file); + + /* validate the length of each of the 3 parts */ + start = buffer; + for (part = 0; part < 3; part++) { + end = strchr(start, delim[part]); + if (end == NULL || (end - start) > max_len[part]) { + free(buffer); + return -1; + } + start = end + 1; + } + + if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) { + err("sscanf"); + free(buffer); + return -1; + } + + free(buffer); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +int usbip_vhci_driver_open(void) +{ + int nports; + struct udev_device *hc_device; + + udev_context = udev_new(); + if (!udev_context) { + err("udev_new failed"); + return -1; + } + + /* will be freed in usbip_driver_close() */ + hc_device = + udev_device_new_from_subsystem_sysname(udev_context, + USBIP_VHCI_BUS_TYPE, + USBIP_VHCI_DEVICE_NAME); + if (!hc_device) { + err("udev_device_new_from_subsystem_sysname failed"); + goto err; + } + + nports = get_nports(hc_device); + if (nports <= 0) { + err("no available ports"); + goto err; + } + dbg("available ports: %d", nports); + + vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver) + + nports * sizeof(struct usbip_imported_device)); + if (!vhci_driver) { + err("vhci_driver allocation failed"); + goto err; + } + + vhci_driver->nports = nports; + vhci_driver->hc_device = hc_device; + vhci_driver->ncontrollers = get_ncontrollers(); + dbg("available controllers: %d", vhci_driver->ncontrollers); + + if (vhci_driver->ncontrollers <=0) { + err("no available usb controllers"); + goto err; + } + + if (refresh_imported_device_list()) + goto err; + + return 0; + +err: + udev_device_unref(hc_device); + + if (vhci_driver) + free(vhci_driver); + + vhci_driver = NULL; + + udev_unref(udev_context); + + return -1; +} + + +void usbip_vhci_driver_close(void) +{ + if (!vhci_driver) + return; + + udev_device_unref(vhci_driver->hc_device); + + free(vhci_driver); + + vhci_driver = NULL; + + udev_unref(udev_context); +} + + +int usbip_vhci_refresh_device_list(void) +{ + + if (refresh_imported_device_list()) + goto err; + + return 0; +err: + dbg("failed to refresh device list"); + return -1; +} + + +int usbip_vhci_get_free_port(uint32_t speed) +{ + for (int i = 0; i < vhci_driver->nports; i++) { + + switch (speed) { + case USB_SPEED_SUPER: + if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER) + continue; + break; + default: + if (vhci_driver->idev[i].hub != HUB_SPEED_HIGH) + continue; + break; + } + + if (vhci_driver->idev[i].status == VDEV_ST_NULL) + return vhci_driver->idev[i].port; + } + + return -1; +} + +int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, + uint32_t speed) { + char buff[200]; /* what size should be ? */ + char attach_attr_path[SYSFS_PATH_MAX]; + char attr_attach[] = "attach"; + const char *path; + int ret; + + snprintf(buff, sizeof(buff), "%u %d %u %u", + port, sockfd, devid, speed); + dbg("writing: %s", buff); + + path = udev_device_get_syspath(vhci_driver->hc_device); + snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s", + path, attr_attach); + dbg("attach attribute path: %s", attach_attr_path); + + ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff)); + if (ret < 0) { + dbg("write_sysfs_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) +{ + char detach_attr_path[SYSFS_PATH_MAX]; + char attr_detach[] = "detach"; + char buff[200]; /* what size should be ? */ + const char *path; + int ret; + + snprintf(buff, sizeof(buff), "%u", port); + dbg("writing: %s", buff); + + path = udev_device_get_syspath(vhci_driver->hc_device); + snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s", + path, attr_detach); + dbg("detach attribute path: %s", detach_attr_path); + + ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff)); + if (ret < 0) { + dbg("write_sysfs_attribute failed"); + return -1; + } + + dbg("detached port: %d", port); + + return 0; +} + +int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev) +{ + char product_name[100]; + char host[NI_MAXHOST] = "unknown host"; + char serv[NI_MAXSERV] = "unknown port"; + char remote_busid[SYSFS_BUS_ID_SIZE]; + int ret; + int read_record_error = 0; + + if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED) + return 0; + + ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv), + remote_busid); + if (ret) { + err("read_record"); + read_record_error = 1; + } + + printf("Port %02d: <%s> at %s\n", idev->port, + usbip_status_string(idev->status), + usbip_speed_string(idev->udev.speed)); + + usbip_names_get_product(product_name, sizeof(product_name), + idev->udev.idVendor, idev->udev.idProduct); + + printf(" %s\n", product_name); + + if (!read_record_error) { + printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid, + host, serv, remote_busid); + printf("%10s -> remote bus/dev %03d/%03d\n", " ", + idev->busnum, idev->devnum); + } else { + printf("%10s -> unknown host, remote port and remote busid\n", + idev->udev.busid); + printf("%10s -> remote bus/dev %03d/%03d\n", " ", + idev->busnum, idev->devnum); + } + + return 0; +} diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h new file mode 100644 index 000000000..6c9aca216 --- /dev/null +++ b/tools/usb/usbip/libsrc/vhci_driver.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#ifndef __VHCI_DRIVER_H +#define __VHCI_DRIVER_H + +#include <libudev.h> +#include <stdint.h> + +#include "usbip_common.h" + +#define USBIP_VHCI_BUS_TYPE "platform" +#define USBIP_VHCI_DEVICE_NAME "vhci_hcd.0" + +enum hub_speed { + HUB_SPEED_HIGH = 0, + HUB_SPEED_SUPER, +}; + +struct usbip_imported_device { + enum hub_speed hub; + uint8_t port; + uint32_t status; + + uint32_t devid; + + uint8_t busnum; + uint8_t devnum; + + /* usbip_class_device list */ + struct usbip_usb_device udev; +}; + +struct usbip_vhci_driver { + + /* /sys/devices/platform/vhci_hcd */ + struct udev_device *hc_device; + + int ncontrollers; + int nports; + struct usbip_imported_device idev[]; +}; + + +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(uint32_t speed); +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); + +int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev); + +#endif /* __VHCI_DRIVER_H */ |