diff options
Diffstat (limited to 'tools/usb/usbip/libsrc')
| -rw-r--r-- | tools/usb/usbip/libsrc/Makefile.am | 8 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/list.h | 136 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/names.c | 504 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/names.h | 41 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/sysfs_utils.c | 31 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/sysfs_utils.h | 8 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/usbip_common.c | 285 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/usbip_common.h | 137 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/usbip_host_driver.c | 280 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/usbip_host_driver.h | 49 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/vhci_driver.c | 411 | ||||
| -rw-r--r-- | tools/usb/usbip/libsrc/vhci_driver.h | 59 | 
12 files changed, 1949 insertions, 0 deletions
diff --git a/tools/usb/usbip/libsrc/Makefile.am b/tools/usb/usbip/libsrc/Makefile.am new file mode 100644 index 000000000000..7c8f8a4d54e4 --- /dev/null +++ b/tools/usb/usbip/libsrc/Makefile.am @@ -0,0 +1,8 @@ +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 \ +		       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 000000000000..8d0c936e184f --- /dev/null +++ b/tools/usb/usbip/libsrc/list.h @@ -0,0 +1,136 @@ +#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_struct 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 000000000000..81ff8522405c --- /dev/null +++ b/tools/usb/usbip/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) +		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 000000000000..680926512de2 --- /dev/null +++ b/tools/usb/usbip/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/tools/usb/usbip/libsrc/sysfs_utils.c b/tools/usb/usbip/libsrc/sysfs_utils.c new file mode 100644 index 000000000000..36ac88ece0b8 --- /dev/null +++ b/tools/usb/usbip/libsrc/sysfs_utils.c @@ -0,0 +1,31 @@ +#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 000000000000..32ac1d105d18 --- /dev/null +++ b/tools/usb/usbip/libsrc/sysfs_utils.h @@ -0,0 +1,8 @@ + +#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 000000000000..ac73710473de --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_common.c @@ -0,0 +1,285 @@ +/* + * 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"; +} + + +#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); +	strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE); + +	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]; +	struct udev_device *sif; + +	sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i); + +	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 000000000000..5a0e95edf4df --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_common.h @@ -0,0 +1,137 @@ +/* + * 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 "../../uapi/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 + +/* kernel module names */ +#define USBIP_CORE_MOD_NAME	"usbip-core" +#define USBIP_HOST_DRV_NAME	"usbip-host" +#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 + +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); + +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_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c new file mode 100644 index 000000000000..bef08d5c44e8 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_driver.c @@ -0,0 +1,280 @@ +/* + * 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 <fcntl.h> + +#include <errno.h> +#include <unistd.h> + +#include <libudev.h> + +#include "usbip_common.h" +#include "usbip_host_driver.h" +#include "list.h" +#include "sysfs_utils.h" + +#undef  PROGNAME +#define PROGNAME "libusbip" + +struct usbip_host_driver *host_driver; +struct udev *udev_context; + +static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) +{ +	char status_attr_path[SYSFS_PATH_MAX]; +	int fd; +	int length; +	char status; +	int value = 0; + +	snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status", +		 udev->path); + +	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); + +	return value; +} + +static +struct usbip_exported_device *usbip_exported_device_new(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; +	} + +	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(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++) +		read_usb_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(void) +{ +	struct usbip_exported_device *edev; +	struct udev_enumerate *enumerate; +	struct udev_list_entry *devices, *dev_list_entry; +	struct udev_device *dev; +	const char *path; +	const char *driver; + +	enumerate = udev_enumerate_new(udev_context); +	udev_enumerate_add_match_subsystem(enumerate, "usb"); +	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-host driver. */ +		driver = udev_device_get_driver(dev); +		if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) { +			edev = usbip_exported_device_new(path); +			if (!edev) { +				dbg("usbip_exported_device_new failed"); +				continue; +			} + +			list_add(&edev->node, &host_driver->edev_list); +			host_driver->ndevs++; +		} +	} + +	return 0; +} + +static void usbip_exported_device_destroy(void) +{ +	struct list_head *i, *tmp; +	struct usbip_exported_device *edev; + +	list_for_each_safe(i, tmp, &host_driver->edev_list) { +		edev = list_entry(i, struct usbip_exported_device, node); +		list_del(i); +		free(edev); +	} +} + +int usbip_host_driver_open(void) +{ +	int rc; + +	udev_context = udev_new(); +	if (!udev_context) { +		err("udev_new failed"); +		return -1; +	} + +	host_driver = calloc(1, sizeof(*host_driver)); + +	host_driver->ndevs = 0; +	INIT_LIST_HEAD(&host_driver->edev_list); + +	rc = refresh_exported_devices(); +	if (rc < 0) +		goto err_free_host_driver; + +	return 0; + +err_free_host_driver: +	free(host_driver); +	host_driver = NULL; + +	udev_unref(udev_context); + +	return -1; +} + +void usbip_host_driver_close(void) +{ +	if (!host_driver) +		return; + +	usbip_exported_device_destroy(); + +	free(host_driver); +	host_driver = NULL; + +	udev_unref(udev_context); +} + +int usbip_host_refresh_device_list(void) +{ +	int rc; + +	usbip_exported_device_destroy(); + +	host_driver->ndevs = 0; +	INIT_LIST_HEAD(&host_driver->edev_list); + +	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 sockfd_attr_path[SYSFS_PATH_MAX]; +	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(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", +		 edev->udev.path, attr_name); + +	snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); + +	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_host_get_device(int num) +{ +	struct list_head *i; +	struct usbip_exported_device *edev; +	int cnt = 0; + +	list_for_each(i, &host_driver->edev_list) { +		edev = list_entry(i, struct usbip_exported_device, node); +		if (num == cnt) +			return edev; +		else +			cnt++; +	} + +	return NULL; +} 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 000000000000..2a31f855c616 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_driver.h @@ -0,0 +1,49 @@ +/* + * 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" +#include "list.h" + +struct usbip_host_driver { +	int ndevs; +	/* list of exported device */ +	struct list_head edev_list; +}; + +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[]; +}; + +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/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c new file mode 100644 index 000000000000..ad9204773533 --- /dev/null +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2005-2007 Takahiro Hirofuchi + */ + +#include "usbip_common.h" +#include "vhci_driver.h" +#include <limits.h> +#include <netdb.h> +#include <libudev.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; + + +	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 %31s\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); + +			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 refresh_imported_device_list(void) +{ +	const char *attr_status; + +	attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, +					       "status"); +	if (!attr_status) { +		err("udev_device_get_sysattr_value failed"); +		return -1; +	} + +	return parse_status(attr_status); +} + +static int get_nports(void) +{ +	char *c; +	int nports = 0; +	const char *attr_status; + +	attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, +					       "status"); +	if (!attr_status) { +		err("udev_device_get_sysattr_value failed"); +		return -1; +	} + +	/* skip a header line */ +	c = strchr(attr_status, '\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; +} + +/* + * 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) +{ +	udev_context = udev_new(); +	if (!udev_context) { +		err("udev_new failed"); +		return -1; +	} + +	vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver)); + +	/* will be freed in usbip_driver_close() */ +	vhci_driver->hc_device = +		udev_device_new_from_subsystem_sysname(udev_context, +						       USBIP_VHCI_BUS_TYPE, +						       USBIP_VHCI_DRV_NAME); +	if (!vhci_driver->hc_device) { +		err("udev_device_new_from_subsystem_sysname failed"); +		goto err; +	} + +	vhci_driver->nports = get_nports(); + +	dbg("available ports: %d", vhci_driver->nports); + +	if (refresh_imported_device_list()) +		goto err; + +	return 0; + +err: +	udev_device_unref(vhci_driver->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(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) { +	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 000000000000..fa2316cf2cac --- /dev/null +++ b/tools/usb/usbip/libsrc/vhci_driver.h @@ -0,0 +1,59 @@ +/* + * 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 MAXNPORT 128 + +struct usbip_imported_device { +	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 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); + +int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev); + +#endif /* __VHCI_DRIVER_H */  | 
