[PATCH v2 01/11] usbip: exporting devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



USB/IP supports a function to import USB devices from application-side 
machine by attach command.
The usage is as following.
dev:# (Physically attach your USB device.)
dev:# insmod usbip-core.ko and usbip-host.ko
dev:# usbipd -D
// Start usbip daemon.
dev:# usbip list -l
// List local USB devices and their busid.
dev:# usbip bind --busid <busid>
// Make a device exportable to other hosts.

app:# insmod usbip-core.ko and vhci-hcd.ko
app:# usbip list --remote <host>
// List importable USB devices from the <host>.
app:# usbip attach --remote <host> --busid <busid>
// Import a device

By attach command, connection will be established from application-side 
to device-side.

This patch introduces a function to export devices form device-side 
machine to application-side machine.
The usage is as following.
app:# insmod usbip-core.ko and vhci-hcd.ko
app:# usbipa -D
// Start usbip daemon.

dev:# (Physically attach your USB device.)
dev:# insmod usbip-core.ko and usbip-host.ko
dev:# usbip list -l
// List local USB devices and their busid.
dev:# usbip connect --remote <host> --busid <busid>
// Export a device to <host>.

For this, export function, connection is established from device-side 
machine to application-side machine.

Following use cases are supposed for the export function.
1) Server application or cloud service serves distributed ubiquitous 
devices.
2) Dedicate devices to server application or cloud service.

To connect to cloud service, it needs to connect from inside of 
firewall.

Probably, the export function was planned because the packets have been 
defined in a header file (usbip_network.h) but it not yet used.
This patch fixes the defined packet structures (ie. int in reply to 
uinit32_t) and use them.

Signed-off-by: Nobuo Iwata <nobuo.iwata@xxxxxxxxxxxxxxx>
---
 tools/usb/usbip/libsrc/usbip_host_driver.c |  16 ++
 tools/usb/usbip/libsrc/usbip_host_driver.h |   1 +
 tools/usb/usbip/libsrc/vhci_driver.c       | 124 +++++++++--
 tools/usb/usbip/libsrc/vhci_driver.h       |   8 +-
 tools/usb/usbip/src/Makefile.am            |   9 +-
 tools/usb/usbip/src/usbip.c                |  17 +-
 tools/usb/usbip/src/usbip.h                |  11 +-
 tools/usb/usbip/src/usbip_attach.c         |  49 +---
 tools/usb/usbip/src/usbip_bind.c           |   7 +-
 tools/usb/usbip/src/usbip_connect.c        | 214 ++++++++++++++++++
 tools/usb/usbip/src/usbip_detach.c         |  13 +-
 tools/usb/usbip/src/usbip_disconnect.c     | 202 +++++++++++++++++
 tools/usb/usbip/src/usbip_list.c           |  22 +-
 tools/usb/usbip/src/usbip_network.h        |   5 +-
 tools/usb/usbip/src/usbip_port.c           |   5 +
 tools/usb/usbip/src/usbip_unbind.c         |   7 +-
 tools/usb/usbip/src/usbipd.c               | 232 +++----------------
 tools/usb/usbip/src/usbipd_app.c           | 240 ++++++++++++++++++++
 tools/usb/usbip/src/usbipd_dev.c           | 247 +++++++++++++++++++++
 19 files changed, 1132 insertions(+), 297 deletions(-)

diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c
index bef08d5..de5541a 100644
--- a/tools/usb/usbip/libsrc/usbip_host_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_host_driver.c
@@ -278,3 +278,19 @@ struct usbip_exported_device *usbip_host_get_device(int num)
 
 	return NULL;
 }
+
+struct usbip_exported_device *usbip_host_find_device(char *busid)
+{
+	struct list_head *i;
+	struct usbip_exported_device *edev;
+
+	list_for_each(i, &host_driver->edev_list) {
+		edev = list_entry(i, struct usbip_exported_device, node);
+		if (!strncmp(busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
+			return edev;
+		}
+	}
+
+	return NULL;
+}
+
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.h b/tools/usb/usbip/libsrc/usbip_host_driver.h
index 2a31f85..69c65a6 100644
--- a/tools/usb/usbip/libsrc/usbip_host_driver.h
+++ b/tools/usb/usbip/libsrc/usbip_host_driver.h
@@ -45,5 +45,6 @@ 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);
+struct usbip_exported_device *usbip_host_find_device(char *busid);
 
 #endif /* __USBIP_HOST_DRIVER_H */
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c
index ad92047..797949d 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.c
+++ b/tools/usb/usbip/libsrc/vhci_driver.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2005-2007 Takahiro Hirofuchi
  */
 
 #include "usbip_common.h"
@@ -7,6 +8,8 @@
 #include <limits.h>
 #include <netdb.h>
 #include <libudev.h>
+#include <fcntl.h>
+#include <errno.h>
 #include "sysfs_utils.h"
 
 #undef  PROGNAME
@@ -215,6 +218,23 @@ static int read_record(int rhport, char *host, unsigned long host_len,
 	return 0;
 }
 
+static int open_hc_device(int reopen)
+{
+	if (reopen) {
+		udev_device_unref(vhci_driver->hc_device);
+	}
+
+	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");
+		return -1;
+	}
+	return 0;
+}
+
 /* ---------------------------------------------------------------------- */
 
 int usbip_vhci_driver_open(void)
@@ -227,14 +247,8 @@ int usbip_vhci_driver_open(void)
 
 	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;
+	if (open_hc_device(0)) {
+		goto err0;
 	}
 
 	vhci_driver->nports = get_nports();
@@ -242,13 +256,13 @@ int usbip_vhci_driver_open(void)
 	dbg("available ports: %d", vhci_driver->nports);
 
 	if (refresh_imported_device_list())
-		goto err;
+		goto err1;
 
 	return 0;
 
-err:
+err1:
 	udev_device_unref(vhci_driver->hc_device);
-
+err0:
 	if (vhci_driver)
 		free(vhci_driver);
 
@@ -277,7 +291,8 @@ void usbip_vhci_driver_close(void)
 
 int usbip_vhci_refresh_device_list(void)
 {
-
+	if (open_hc_device(1))
+		goto err;
 	if (refresh_imported_device_list())
 		goto err;
 
@@ -298,6 +313,34 @@ int usbip_vhci_get_free_port(void)
 	return -1;
 }
 
+struct usbip_imported_device *usbip_vhci_get_device(int port)
+{
+	for (int i = 0; i < vhci_driver->nports; i++) {
+		if (vhci_driver->idev[i].port == port)
+			return vhci_driver->idev + i;
+	}
+	return NULL;
+}
+
+struct usbip_imported_device *usbip_vhci_find_device(char *host, char *busid)
+{
+	int ret;
+	char rhost[NI_MAXHOST] = "unknown host";
+	char rserv[NI_MAXSERV] = "unknown port";
+	char rbusid[SYSFS_BUS_ID_SIZE];
+
+	for (int i = 0; i < vhci_driver->nports; i++) {
+		ret = read_record(vhci_driver->idev[i].port, rhost, NI_MAXHOST,
+				rserv, NI_MAXSERV, rbusid);
+		if (!ret &&
+			!strncmp(host, rhost, NI_MAXHOST) &&
+			!strncmp(busid, rbusid, SYSFS_BUS_ID_SIZE)) {
+			return vhci_driver->idev + i;
+		}
+	}
+	return NULL;
+}
+
 int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 		uint32_t speed) {
 	char buff[200]; /* what size should be ? */
@@ -409,3 +452,58 @@ int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
 
 	return 0;
 }
+
+#define MAX_BUFF 100
+int usbip_vhci_create_record(char *host, char *port, char *busid, int rhport)
+{
+	int fd;
+	char path[PATH_MAX+1];
+	char buff[MAX_BUFF+1];
+	int ret;
+
+	ret = mkdir(VHCI_STATE_PATH, 0700);
+	if (ret < 0) {
+		/* if VHCI_STATE_PATH exists, then it better be a directory */
+		if (errno == EEXIST) {
+			struct stat s;
+
+			ret = stat(VHCI_STATE_PATH, &s);
+			if (ret < 0)
+				return -1;
+			if (!(s.st_mode & S_IFDIR))
+				return -1;
+		} else
+			return -1;
+	}
+
+	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+	fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
+	if (fd < 0)
+		return -1;
+
+	snprintf(buff, MAX_BUFF, "%s %s %s\n",
+			host, port, busid);
+
+	ret = write(fd, buff, strlen(buff));
+	if (ret != (ssize_t) strlen(buff)) {
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+
+	return 0;
+}
+
+int usbip_vhci_delete_record(int rhport)
+{
+	char path[PATH_MAX+1];
+
+	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+	remove(path);
+	rmdir(VHCI_STATE_PATH);
+
+	return 0;
+}
diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h
index fa2316c..2a9e9c1 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.h
+++ b/tools/usb/usbip/libsrc/vhci_driver.h
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2005-2007 Takahiro Hirofuchi
  */
 
 #ifndef __VHCI_DRIVER_H
@@ -45,6 +46,8 @@ int  usbip_vhci_refresh_device_list(void);
 
 
 int usbip_vhci_get_free_port(void);
+struct usbip_imported_device *usbip_vhci_get_device(int port);
+struct usbip_imported_device *usbip_vhci_find_device(char *host, char *busid);
 int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
 		uint32_t speed);
 
@@ -54,6 +57,9 @@ int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
 
 int usbip_vhci_detach_device(uint8_t port);
 
+int usbip_vhci_create_record(char *host, char *port, char *busid, int rhport);
+int usbip_vhci_delete_record(int rhport);
+
 int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev);
 
 #endif /* __VHCI_DRIVER_H */
diff --git a/tools/usb/usbip/src/Makefile.am b/tools/usb/usbip/src/Makefile.am
index e81a4eb..f5697c2 100644
--- a/tools/usb/usbip/src/Makefile.am
+++ b/tools/usb/usbip/src/Makefile.am
@@ -2,10 +2,13 @@ AM_CPPFLAGS = -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
 AM_CFLAGS   = @EXTRA_CFLAGS@
 LDADD       = $(top_builddir)/libsrc/libusbip.la
 
-sbin_PROGRAMS := usbip usbipd
+sbin_PROGRAMS := usbip usbipd usbipa
 
 usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
 		 usbip_attach.c usbip_detach.c usbip_list.c \
-		 usbip_bind.c usbip_unbind.c usbip_port.c
+		 usbip_bind.c usbip_unbind.c usbip_port.c \
+		 usbip_connect.c usbip_disconnect.c
 
-usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
+usbipd_SOURCES := usbip_network.h usbipd.c usbipd_dev.c usbip_network.c
+
+usbipa_SOURCES := usbip_network.h usbipd.c usbipd_app.c usbip_network.c
diff --git a/tools/usb/usbip/src/usbip.c b/tools/usb/usbip/src/usbip.c
index d7599d9..9d1468f 100644
--- a/tools/usb/usbip/src/usbip.c
+++ b/tools/usb/usbip/src/usbip.c
@@ -2,7 +2,8 @@
  * command structure borrowed from udev
  * (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
  *
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -76,6 +77,18 @@ static const struct command cmds[] = {
 		.usage = usbip_detach_usage
 	},
 	{
+		.name  = "connect",
+		.fn    = usbip_connect,
+		.help  = "Connect a USB device to a remote computer",
+		.usage = usbip_connect_usage
+	},
+	{
+		.name  = "disconnect",
+		.fn    = usbip_disconnect,
+		.help  = "Disconnect a USB device from a remote computer",
+		.usage = usbip_disconnect_usage
+	},
+	{
 		.name  = "list",
 		.fn    = usbip_list,
 		.help  = "List exportable or local USB devices",
@@ -97,7 +110,7 @@ static const struct command cmds[] = {
 		.name  = "port",
 		.fn    = usbip_port_show,
 		.help  = "Show imported USB devices",
-		.usage = NULL
+		.usage = usbip_port_usage
 	},
 	{ NULL, NULL, NULL, NULL }
 };
diff --git a/tools/usb/usbip/src/usbip.h b/tools/usb/usbip/src/usbip.h
index 84fe66a..0875d15 100644
--- a/tools/usb/usbip/src/usbip.h
+++ b/tools/usb/usbip/src/usbip.h
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -30,11 +31,19 @@ int usbip_list(int argc, char *argv[]);
 int usbip_bind(int argc, char *argv[]);
 int usbip_unbind(int argc, char *argv[]);
 int usbip_port_show(int argc, char *argv[]);
+int usbip_connect(int argc, char *argv[]);
+int usbip_disconnect(int argc, char *argv[]);
+
+int usbip_bind_device(char *busid);
+int usbip_unbind_device(char *busid);
 
 void usbip_attach_usage(void);
 void usbip_detach_usage(void);
 void usbip_list_usage(void);
 void usbip_bind_usage(void);
 void usbip_unbind_usage(void);
+void usbip_port_usage(void);
+void usbip_connect_usage(void);
+void usbip_disconnect_usage(void);
 
 #endif /* __USBIP_H */
diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c
index d58a14d..0817e92 100644
--- a/tools/usb/usbip/src/usbip_attach.c
+++ b/tools/usb/usbip/src/usbip_attach.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -43,49 +44,6 @@ void usbip_attach_usage(void)
 	printf("usage: %s", usbip_attach_usage_string);
 }
 
-#define MAX_BUFF 100
-static int record_connection(char *host, char *port, char *busid, int rhport)
-{
-	int fd;
-	char path[PATH_MAX+1];
-	char buff[MAX_BUFF+1];
-	int ret;
-
-	ret = mkdir(VHCI_STATE_PATH, 0700);
-	if (ret < 0) {
-		/* if VHCI_STATE_PATH exists, then it better be a directory */
-		if (errno == EEXIST) {
-			struct stat s;
-
-			ret = stat(VHCI_STATE_PATH, &s);
-			if (ret < 0)
-				return -1;
-			if (!(s.st_mode & S_IFDIR))
-				return -1;
-		} else
-			return -1;
-	}
-
-	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
-
-	fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
-	if (fd < 0)
-		return -1;
-
-	snprintf(buff, MAX_BUFF, "%s %s %s\n",
-			host, port, busid);
-
-	ret = write(fd, buff, strlen(buff));
-	if (ret != (ssize_t) strlen(buff)) {
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-
-	return 0;
-}
-
 static int import_device(int sockfd, struct usbip_usb_device *udev)
 {
 	int rc;
@@ -184,12 +142,13 @@ static int attach_device(char *host, char *busid)
 	rhport = query_import_device(sockfd, busid);
 	if (rhport < 0) {
 		err("query");
+		close(sockfd);
 		return -1;
 	}
 
 	close(sockfd);
 
-	rc = record_connection(host, usbip_port_string, busid, rhport);
+	rc = usbip_vhci_create_record(host, usbip_port_string, busid, rhport);
 	if (rc < 0) {
 		err("record connection");
 		return -1;
diff --git a/tools/usb/usbip/src/usbip_bind.c b/tools/usb/usbip/src/usbip_bind.c
index fa46141..1c09338 100644
--- a/tools/usb/usbip/src/usbip_bind.c
+++ b/tools/usb/usbip/src/usbip_bind.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -139,7 +140,7 @@ out:
 	return status;
 }
 
-static int bind_device(char *busid)
+int usbip_bind_device(char *busid)
 {
 	int rc;
 	struct udev *udev;
@@ -200,7 +201,7 @@ int usbip_bind(int argc, char *argv[])
 
 		switch (opt) {
 		case 'b':
-			ret = bind_device(optarg);
+			ret = usbip_bind_device(optarg);
 			goto out;
 		default:
 			goto err_out;
diff --git a/tools/usb/usbip/src/usbip_connect.c b/tools/usb/usbip/src/usbip_connect.c
new file mode 100644
index 0000000..b30cccf
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_connect.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ *               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/stat.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "usbip_host_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_connect_usage_string[] =
+	"usbip connect <args>\n"
+	"    -r, --remote=<host>    Address of a remote computer\n"
+	"    -b, --busid=<busid>    Bus ID of a device to be connected\n";
+
+void usbip_connect_usage(void)
+{
+	printf("usage: %s", usbip_connect_usage_string);
+}
+
+static int send_export_device(int sockfd, struct usbip_usb_device *udev)
+{
+	int rc;
+	struct op_export_request request;
+	struct op_export_reply   reply;
+	uint16_t code = OP_REP_EXPORT;
+
+	memset(&request, 0, sizeof(request));
+	memset(&reply, 0, sizeof(reply));
+
+	/* send a request */
+	rc = usbip_net_send_op_common(sockfd, OP_REQ_EXPORT, 0);
+	if (rc < 0) {
+		err("send op_common");
+		return -1;
+	}
+
+	memcpy(&request.udev, udev, sizeof(struct usbip_usb_device));
+
+	PACK_OP_EXPORT_REQUEST(0, &request);
+
+	rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
+	if (rc < 0) {
+		err("send op_export_request");
+		return -1;
+	}
+
+	/* receive a reply */
+	rc = usbip_net_recv_op_common(sockfd, &code);
+	if (rc < 0) {
+		err("recv op_common");
+		return -1;
+	}
+
+	rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
+	if (rc < 0) {
+		err("recv op_export_reply");
+		return -1;
+	}
+
+	PACK_OP_EXPORT_REPLY(0, &reply);
+
+	/* check the reply */
+	if (reply.returncode) {
+		err("recv error return %d", reply.returncode);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int export_device(char *busid, int sockfd)
+{
+	int rc;
+	struct usbip_exported_device *edev;
+
+	rc = usbip_host_driver_open();
+	if (rc < 0) {
+		err("open host_driver");
+		return -1;
+	}
+
+	rc = usbip_host_refresh_device_list();
+	if (rc < 0) {
+		err("could not refresh device list");
+		usbip_host_driver_close();
+		return -1;
+	}
+
+	edev = usbip_host_find_device(busid);
+	if (edev == NULL) {
+		err("find device");
+		usbip_host_driver_close();
+		return -1;
+	}
+
+	rc = send_export_device(sockfd, &edev->udev);
+	if (rc < 0) {
+		err("send export");
+		usbip_host_driver_close();
+		return -1;
+	}
+
+	rc = usbip_host_export_device(edev, sockfd);
+	if (rc < 0) {
+		err("export device");
+		usbip_host_driver_close();
+		return -1;
+	}
+
+	usbip_host_driver_close();
+
+	return 0;
+}
+
+static int connect_device(char *host, char *busid)
+{
+	int sockfd;
+	int rc;
+
+	rc = usbip_bind_device(busid);
+	if (rc) {
+		err("bind");
+		goto err0;
+	}
+
+	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	if (sockfd < 0) {
+		err("tcp connect");
+		goto err1;
+	}
+
+	rc = export_device(busid, sockfd);
+	if (rc < 0) {
+		err("export");
+		goto err2;
+	}
+
+	close(sockfd);
+
+	return 0;
+err2:
+	close(sockfd);
+err1:
+	usbip_unbind_device(busid);
+err0:
+	return -1;
+}
+
+int usbip_connect(int argc, char *argv[])
+{
+	static const struct option opts[] = {
+		{ "remote", required_argument, NULL, 'r' },
+		{ "busid",  required_argument, NULL, 'b' },
+		{ NULL, 0,  NULL, 0 }
+	};
+	char *host = NULL;
+	char *busid = NULL;
+	int opt;
+	int ret = -1;
+
+	for (;;) {
+		opt = getopt_long(argc, argv, "r:b:", opts, NULL);
+
+		if (opt == -1)
+			break;
+
+		switch (opt) {
+		case 'r':
+			host = optarg;
+			break;
+		case 'b':
+			busid = optarg;
+			break;
+		default:
+			goto err_out;
+		}
+	}
+
+	if (!host || !busid)
+		goto err_out;
+
+	ret = connect_device(host, busid);
+	goto out;
+
+err_out:
+	usbip_connect_usage();
+out:
+	return ret;
+}
diff --git a/tools/usb/usbip/src/usbip_detach.c b/tools/usb/usbip/src/usbip_detach.c
index 05c6d15..a2a187d 100644
--- a/tools/usb/usbip/src/usbip_detach.c
+++ b/tools/usb/usbip/src/usbip_detach.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -45,7 +46,6 @@ static int detach_port(char *port)
 {
 	int ret;
 	uint8_t portnum;
-	char path[PATH_MAX+1];
 
 	for (unsigned int i = 0; i < strlen(port); i++)
 		if (!isdigit(port[i])) {
@@ -59,10 +59,7 @@ static int detach_port(char *port)
 
 	/* remove the port state file */
 
-	snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum);
-
-	remove(path);
-	rmdir(VHCI_STATE_PATH);
+	usbip_vhci_delete_record(portnum);
 
 	ret = usbip_vhci_driver_open();
 	if (ret < 0) {
@@ -71,8 +68,10 @@ static int detach_port(char *port)
 	}
 
 	ret = usbip_vhci_detach_device(portnum);
-	if (ret < 0)
+	if (ret < 0) {
+		usbip_vhci_driver_close();
 		return -1;
+	}
 
 	usbip_vhci_driver_close();
 
diff --git a/tools/usb/usbip/src/usbip_disconnect.c b/tools/usb/usbip/src/usbip_disconnect.c
new file mode 100644
index 0000000..b9cbe1d
--- /dev/null
+++ b/tools/usb/usbip/src/usbip_disconnect.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ *               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/stat.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "usbip_host_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_disconnect_usage_string[] =
+	"usbip disconnect <args>\n"
+	"    -r, --remote=<host>    Address of a remote computer\n"
+	"    -b, --busid=<busid>    Bus ID of a device to be disconnected\n";
+
+void usbip_disconnect_usage(void)
+{
+	printf("usage: %s", usbip_disconnect_usage_string);
+}
+
+static int send_unexport_device(int sockfd, struct usbip_usb_device *udev)
+{
+	int rc;
+	struct op_unexport_request request;
+	struct op_unexport_reply   reply;
+	uint16_t code = OP_REP_UNEXPORT;
+
+	memset(&request, 0, sizeof(request));
+	memset(&reply, 0, sizeof(reply));
+
+	/* send a request */
+	rc = usbip_net_send_op_common(sockfd, OP_REQ_UNEXPORT, 0);
+	if (rc < 0) {
+		err("send op_common");
+		return -1;
+	}
+
+	memcpy(&request.udev, udev, sizeof(struct usbip_usb_device));
+
+	PACK_OP_UNEXPORT_REQUEST(0, &request);
+
+	rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
+	if (rc < 0) {
+		err("send op_export_request");
+		return -1;
+	}
+
+	/* receive a reply */
+	rc = usbip_net_recv_op_common(sockfd, &code);
+	if (rc < 0) {
+		err("recv op_common");
+		return -1;
+	}
+
+	rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
+	if (rc < 0) {
+		err("recv op_unexport_reply");
+		return -1;
+	}
+
+	PACK_OP_EXPORT_REPLY(0, &reply);
+
+	/* check the reply */
+	if (reply.returncode) {
+		err("recv error return %d", reply.returncode);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int unexport_device(char *busid, int sockfd)
+{
+	int rc;
+	struct usbip_exported_device *edev;
+
+	rc = usbip_host_driver_open();
+	if (rc < 0) {
+		err("open host_driver");
+		return -1;
+	}
+
+	rc = usbip_host_refresh_device_list();
+	if (rc < 0) {
+		err("could not refresh device list");
+		usbip_host_driver_close();
+		return -1;
+	}
+
+	edev = usbip_host_find_device(busid);
+	if (edev == NULL) {
+		err("find device");
+		usbip_host_driver_close();
+		return -1;
+	}
+
+	rc = send_unexport_device(sockfd, &edev->udev);
+	if (rc < 0) {
+		err("send unexport");
+		usbip_host_driver_close();
+		return -1;
+	}
+
+	usbip_host_driver_close();
+
+	return 0;
+}
+
+static int disconnect_device(char *host, char *busid)
+{
+	int sockfd;
+	int rc;
+
+	sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+	if (sockfd < 0) {
+		err("tcp connect");
+		return -1;
+	}
+
+	rc = unexport_device(busid, sockfd);
+	if (rc < 0) {
+		err("unexport");
+		close(sockfd);
+		return -1;
+	}
+
+	close(sockfd);
+
+	rc = usbip_unbind_device(busid);
+	if (rc) {
+		err("unbind");
+		return -1;
+	}
+
+	return 0;
+}
+
+int usbip_disconnect(int argc, char *argv[])
+{
+	static const struct option opts[] = {
+		{ "remote", required_argument, NULL, 'r' },
+		{ "busid",  required_argument, NULL, 'b' },
+		{ NULL, 0,  NULL, 0 }
+	};
+	char *host = NULL;
+	char *busid = NULL;
+	int opt;
+	int ret = -1;
+
+	for (;;) {
+		opt = getopt_long(argc, argv, "r:b:", opts, NULL);
+
+		if (opt == -1)
+			break;
+
+		switch (opt) {
+		case 'r':
+			host = optarg;
+			break;
+		case 'b':
+			busid = optarg;
+			break;
+		default:
+			goto err_out;
+		}
+	}
+
+	if (!host || !busid)
+		goto err_out;
+
+	ret = disconnect_device(host, busid);
+	goto out;
+
+err_out:
+	usbip_disconnect_usage();
+out:
+	return ret;
+}
diff --git a/tools/usb/usbip/src/usbip_list.c b/tools/usb/usbip/src/usbip_list.c
index d5ce34a..acbd9b5 100644
--- a/tools/usb/usbip/src/usbip_list.c
+++ b/tools/usb/usbip/src/usbip_list.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -35,9 +36,9 @@
 #include "usbip.h"
 
 static const char usbip_list_usage_string[] =
-	"usbip list [-p|--parsable] <args>\n"
+	"usbip list <args>\n"
 	"    -p, --parsable         Parsable list format\n"
-	"    -r, --remote=<host>    List the exportable USB devices on <host>\n"
+	"    -r, --remote=<host>    List the importable USB devices on <host>\n"
 	"    -l, --local            List the local USB devices\n";
 
 void usbip_list_usage(void)
@@ -45,7 +46,7 @@ void usbip_list_usage(void)
 	printf("usage: %s", usbip_list_usage_string);
 }
 
-static int get_exported_devices(char *host, int sockfd)
+static int get_importable_devices(char *host, int sockfd)
 {
 	char product_name[100];
 	char class_name[100];
@@ -75,14 +76,14 @@ static int get_exported_devices(char *host, int sockfd)
 		return -1;
 	}
 	PACK_OP_DEVLIST_REPLY(0, &reply);
-	dbg("exportable devices: %d\n", reply.ndev);
+	dbg("importable devices: %d\n", reply.ndev);
 
 	if (reply.ndev == 0) {
-		info("no exportable devices found on %s", host);
+		info("no importable devices found on %s", host);
 		return 0;
 	}
 
-	printf("Exportable USB devices\n");
+	printf("Importable USB devices\n");
 	printf("======================\n");
 	printf(" - %s\n", host);
 
@@ -127,7 +128,7 @@ static int get_exported_devices(char *host, int sockfd)
 	return 0;
 }
 
-static int list_exported_devices(char *host)
+static int list_importable_devices(char *host)
 {
 	int rc;
 	int sockfd;
@@ -140,9 +141,10 @@ static int list_exported_devices(char *host)
 	}
 	dbg("connected to %s:%s", host, usbip_port_string);
 
-	rc = get_exported_devices(host, sockfd);
+	rc = get_importable_devices(host, sockfd);
 	if (rc < 0) {
 		err("failed to get device list from %s", host);
+		close(sockfd);
 		return -1;
 	}
 
@@ -264,7 +266,7 @@ int usbip_list(int argc, char *argv[])
 			parsable = true;
 			break;
 		case 'r':
-			ret = list_exported_devices(optarg);
+			ret = list_importable_devices(optarg);
 			goto out;
 		case 'l':
 			ret = list_devices(parsable);
diff --git a/tools/usb/usbip/src/usbip_network.h b/tools/usb/usbip/src/usbip_network.h
index c1e875c..2fe806d 100644
--- a/tools/usb/usbip/src/usbip_network.h
+++ b/tools/usb/usbip/src/usbip_network.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  */
 
 #ifndef __USBIP_NETWORK_H
@@ -93,7 +94,7 @@ struct op_export_request {
 } __attribute__((packed));
 
 struct op_export_reply {
-	int returncode;
+	uint32_t returncode;
 } __attribute__((packed));
 
 
@@ -115,7 +116,7 @@ struct op_unexport_request {
 } __attribute__((packed));
 
 struct op_unexport_reply {
-	int returncode;
+	uint32_t returncode;
 } __attribute__((packed));
 
 #define PACK_OP_UNEXPORT_REQUEST(pack, request)  do {\
diff --git a/tools/usb/usbip/src/usbip_port.c b/tools/usb/usbip/src/usbip_port.c
index a2e884f..7ca120c 100644
--- a/tools/usb/usbip/src/usbip_port.c
+++ b/tools/usb/usbip/src/usbip_port.c
@@ -16,6 +16,11 @@
 #include "vhci_driver.h"
 #include "usbip_common.h"
 
+void usbip_port_usage(void)
+{
+	printf("usage: usbip port\n");
+}
+
 static int list_imported_devices(void)
 {
 	int i;
diff --git a/tools/usb/usbip/src/usbip_unbind.c b/tools/usb/usbip/src/usbip_unbind.c
index a4a496c..cc1ff26 100644
--- a/tools/usb/usbip/src/usbip_unbind.c
+++ b/tools/usb/usbip/src/usbip_unbind.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -39,7 +40,7 @@ void usbip_unbind_usage(void)
 	printf("usage: %s", usbip_unbind_usage_string);
 }
 
-static int unbind_device(char *busid)
+int usbip_unbind_device(char *busid)
 {
 	char bus_type[] = "usb";
 	int rc, ret = -1;
@@ -127,7 +128,7 @@ int usbip_unbind(int argc, char *argv[])
 
 		switch (opt) {
 		case 'b':
-			ret = unbind_device(optarg);
+			ret = usbip_unbind_device(optarg);
 			goto out;
 		default:
 			goto err_out;
diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c
index 2a7cd2b..c63a76f 100644
--- a/tools/usb/usbip/src/usbipd.c
+++ b/tools/usb/usbip/src/usbipd.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
  *               2005-2007 Takahiro Hirofuchi
  *
  * This program is free software: you can redistribute it and/or modify
@@ -40,23 +41,22 @@
 #include <signal.h>
 #include <poll.h>
 
-#include "usbip_host_driver.h"
 #include "usbip_common.h"
 #include "usbip_network.h"
 #include "list.h"
 
-#undef  PROGNAME
-#define PROGNAME "usbipd"
+extern char *usbip_progname;
+
 #define MAXSOCKFD 20
 
 #define MAIN_LOOP_TIMEOUT 10
 
-#define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid"
+extern char *usbip_default_pid_file;
 
 static const char usbip_version_string[] = PACKAGE_STRING;
 
 static const char usbipd_help_string[] =
-	"usage: usbipd [options]\n"
+	"usage: %s [options]\n"
 	"\n"
 	"	-4, --ipv4\n"
 	"		Bind to IPv4. Default is both.\n"
@@ -72,7 +72,7 @@ static const char usbipd_help_string[] =
 	"\n"
 	"	-PFILE, --pid FILE\n"
 	"		Write process id to FILE.\n"
-	"		If no FILE specified, use " DEFAULT_PID_FILE "\n"
+	"		If no FILE specified, use %s.\n"
 	"\n"
 	"	-tPORT, --tcp-port PORT\n"
 	"		Listen on TCP/IP port PORT.\n"
@@ -85,194 +85,7 @@ static const char usbipd_help_string[] =
 
 static void usbipd_help(void)
 {
-	printf("%s\n", usbipd_help_string);
-}
-
-static int recv_request_import(int sockfd)
-{
-	struct op_import_request req;
-	struct usbip_exported_device *edev;
-	struct usbip_usb_device pdu_udev;
-	struct list_head *i;
-	int found = 0;
-	int error = 0;
-	int rc;
-
-	memset(&req, 0, sizeof(req));
-
-	rc = usbip_net_recv(sockfd, &req, sizeof(req));
-	if (rc < 0) {
-		dbg("usbip_net_recv failed: import request");
-		return -1;
-	}
-	PACK_OP_IMPORT_REQUEST(0, &req);
-
-	list_for_each(i, &host_driver->edev_list) {
-		edev = list_entry(i, struct usbip_exported_device, node);
-		if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
-			info("found requested device: %s", req.busid);
-			found = 1;
-			break;
-		}
-	}
-
-	if (found) {
-		/* should set TCP_NODELAY for usbip */
-		usbip_net_set_nodelay(sockfd);
-
-		/* export device needs a TCP/IP socket descriptor */
-		rc = usbip_host_export_device(edev, sockfd);
-		if (rc < 0)
-			error = 1;
-	} else {
-		info("requested device not found: %s", req.busid);
-		error = 1;
-	}
-
-	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
-				      (!error ? ST_OK : ST_NA));
-	if (rc < 0) {
-		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
-		return -1;
-	}
-
-	if (error) {
-		dbg("import request busid %s: failed", req.busid);
-		return -1;
-	}
-
-	memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
-	usbip_net_pack_usb_device(1, &pdu_udev);
-
-	rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
-	if (rc < 0) {
-		dbg("usbip_net_send failed: devinfo");
-		return -1;
-	}
-
-	dbg("import request busid %s: complete", req.busid);
-
-	return 0;
-}
-
-static int send_reply_devlist(int connfd)
-{
-	struct usbip_exported_device *edev;
-	struct usbip_usb_device pdu_udev;
-	struct usbip_usb_interface pdu_uinf;
-	struct op_devlist_reply reply;
-	struct list_head *j;
-	int rc, i;
-
-	reply.ndev = 0;
-	/* number of exported devices */
-	list_for_each(j, &host_driver->edev_list) {
-		reply.ndev += 1;
-	}
-	info("exportable devices: %d", reply.ndev);
-
-	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
-	if (rc < 0) {
-		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
-		return -1;
-	}
-	PACK_OP_DEVLIST_REPLY(1, &reply);
-
-	rc = usbip_net_send(connfd, &reply, sizeof(reply));
-	if (rc < 0) {
-		dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
-		return -1;
-	}
-
-	list_for_each(j, &host_driver->edev_list) {
-		edev = list_entry(j, struct usbip_exported_device, node);
-		dump_usb_device(&edev->udev);
-		memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
-		usbip_net_pack_usb_device(1, &pdu_udev);
-
-		rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
-		if (rc < 0) {
-			dbg("usbip_net_send failed: pdu_udev");
-			return -1;
-		}
-
-		for (i = 0; i < edev->udev.bNumInterfaces; i++) {
-			dump_usb_interface(&edev->uinf[i]);
-			memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
-			usbip_net_pack_usb_interface(1, &pdu_uinf);
-
-			rc = usbip_net_send(connfd, &pdu_uinf,
-					sizeof(pdu_uinf));
-			if (rc < 0) {
-				err("usbip_net_send failed: pdu_uinf");
-				return -1;
-			}
-		}
-	}
-
-	return 0;
-}
-
-static int recv_request_devlist(int connfd)
-{
-	struct op_devlist_request req;
-	int rc;
-
-	memset(&req, 0, sizeof(req));
-
-	rc = usbip_net_recv(connfd, &req, sizeof(req));
-	if (rc < 0) {
-		dbg("usbip_net_recv failed: devlist request");
-		return -1;
-	}
-
-	rc = send_reply_devlist(connfd);
-	if (rc < 0) {
-		dbg("send_reply_devlist failed");
-		return -1;
-	}
-
-	return 0;
-}
-
-static int recv_pdu(int connfd)
-{
-	uint16_t code = OP_UNSPEC;
-	int ret;
-
-	ret = usbip_net_recv_op_common(connfd, &code);
-	if (ret < 0) {
-		dbg("could not receive opcode: %#0x", code);
-		return -1;
-	}
-
-	ret = usbip_host_refresh_device_list();
-	if (ret < 0) {
-		dbg("could not refresh device list: %d", ret);
-		return -1;
-	}
-
-	info("received request: %#0x(%d)", code, connfd);
-	switch (code) {
-	case OP_REQ_DEVLIST:
-		ret = recv_request_devlist(connfd);
-		break;
-	case OP_REQ_IMPORT:
-		ret = recv_request_import(connfd);
-		break;
-	case OP_REQ_DEVINFO:
-	case OP_REQ_CRYPKEY:
-	default:
-		err("received an unknown opcode: %#0x", code);
-		ret = -1;
-	}
-
-	if (ret == 0)
-		info("request %#0x(%d): complete", code, connfd);
-	else
-		info("request %#0x(%d): failed", code, connfd);
-
-	return ret;
+	printf(usbipd_help_string, usbip_progname, usbip_default_pid_file);
 }
 
 #ifdef HAVE_LIBWRAP
@@ -291,12 +104,11 @@ static int tcpd_auth(int connfd)
 }
 #endif
 
-static int do_accept(int listenfd)
+static int do_accept(int listenfd, char *host, char *port)
 {
 	int connfd;
 	struct sockaddr_storage ss;
 	socklen_t len = sizeof(ss);
-	char host[NI_MAXHOST], port[NI_MAXSERV];
 	int rc;
 
 	memset(&ss, 0, sizeof(ss));
@@ -307,8 +119,8 @@ static int do_accept(int listenfd)
 		return -1;
 	}
 
-	rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
-			 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
+	rc = getnameinfo((struct sockaddr *)&ss, len, host, NI_MAXHOST,
+			 port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
 	if (rc)
 		err("getnameinfo: %s", gai_strerror(rc));
 
@@ -325,18 +137,21 @@ static int do_accept(int listenfd)
 	return connfd;
 }
 
+extern int usbip_recv_pdu(int connfd, char *host, char *port);
+
 int process_request(int listenfd)
 {
 	pid_t childpid;
 	int connfd;
+	char host[NI_MAXHOST], port[NI_MAXSERV];
 
-	connfd = do_accept(listenfd);
+	connfd = do_accept(listenfd, host, port);
 	if (connfd < 0)
 		return -1;
 	childpid = fork();
 	if (childpid == 0) {
 		close(listenfd);
-		recv_pdu(connfd);
+		usbip_recv_pdu(connfd, host, port);
 		exit(0);
 	}
 	close(connfd);
@@ -481,6 +296,9 @@ static void remove_pid_file(void)
 	}
 }
 
+extern int usbip_driver_open(void);
+extern void usbip_driver_close(void);
+
 static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 {
 	struct addrinfo *ai_head;
@@ -491,7 +309,7 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	struct timespec timeout;
 	sigset_t sigmask;
 
-	if (usbip_host_driver_open()) {
+	if (usbip_driver_open()) {
 		err("please load " USBIP_CORE_MOD_NAME ".ko and "
 		    USBIP_HOST_DRV_NAME ".ko!");
 		return -1;
@@ -500,7 +318,7 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	if (daemonize) {
 		if (daemon(0, 0) < 0) {
 			err("daemonizing failed: %s", strerror(errno));
-			usbip_host_driver_close();
+			usbip_driver_close();
 			return -1;
 		}
 		umask(0);
@@ -525,7 +343,7 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 
 	ai_head = do_getaddrinfo(NULL, family);
 	if (!ai_head) {
-		usbip_host_driver_close();
+		usbip_driver_close();
 		return -1;
 	}
 	nsockfd = listen_all_addrinfo(ai_head, sockfdlist,
@@ -533,7 +351,7 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 	freeaddrinfo(ai_head);
 	if (nsockfd <= 0) {
 		err("failed to open a listening socket");
-		usbip_host_driver_close();
+		usbip_driver_close();
 		return -1;
 	}
 
@@ -574,7 +392,7 @@ static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
 
 	info("shutting down " PROGNAME);
 	free(fds);
-	usbip_host_driver_close();
+	usbip_driver_close();
 
 	return 0;
 }
@@ -636,7 +454,7 @@ int main(int argc, char *argv[])
 			cmd = cmd_help;
 			break;
 		case 'P':
-			pid_file = optarg ? optarg : DEFAULT_PID_FILE;
+			pid_file = optarg ? optarg : usbip_default_pid_file;
 			break;
 		case 't':
 			usbip_setup_port_number(optarg);
diff --git a/tools/usb/usbip/src/usbipd_app.c b/tools/usb/usbip/src/usbipd_app.c
new file mode 100644
index 0000000..f029b64
--- /dev/null
+++ b/tools/usb/usbip/src/usbipd_app.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ *               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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+
+char *usbip_progname = "usbipa";
+char *usbip_default_pid_file = "/var/run/usbipa";
+
+int usbip_driver_open(void)
+{
+	if (usbip_vhci_driver_open()) {
+		err("please load " USBIP_CORE_MOD_NAME ".ko and "
+			USBIP_VHCI_DRV_NAME ".ko!");
+		return -1;
+	}
+	return 0;
+}
+
+void usbip_driver_close(void)
+{
+	usbip_vhci_driver_close();
+}
+
+static int import_device(int sockfd, struct usbip_usb_device *udev)
+{
+	int rc;
+	int port;
+
+	dbg("Sockfd:%d", sockfd);
+	port = usbip_vhci_get_free_port();
+	if (port < 0) {
+		err("no free port");
+		return -1;
+	}
+
+	dump_usb_device(udev);
+	rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
+					udev->devnum, udev->speed);
+	if (rc < 0) {
+		err("import device");
+		return -1;
+	}
+
+	return port;
+}
+
+static int recv_request_export(int sockfd, char *host, char *port)
+{
+	struct op_export_request req;
+	struct op_export_reply reply;
+	int rhport = 0;
+	int error = 0;
+	int rc;
+
+	memset(&req, 0, sizeof(req));
+	memset(&reply, 0, sizeof(reply));
+
+	rc = usbip_net_recv(sockfd, &req, sizeof(req));
+	if (rc < 0) {
+		dbg("usbip_net_recv failed: export request");
+		return -1;
+	}
+	PACK_OP_EXPORT_REQUEST(0, &req);
+
+	rhport = import_device(sockfd, &req.udev);
+	if (rhport < 0) {
+		dbg("export request busid %s: failed", req.udev.busid);
+		error = 1;
+	}
+
+	rc = usbip_net_send_op_common(sockfd, OP_REP_EXPORT,
+				      (!error ? ST_OK : ST_NA));
+	if (rc < 0) {
+		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_EXPORT);
+		return -1;
+	}
+
+	if (!error) {
+		reply.returncode = 0;
+	} else {
+		reply.returncode = -1;
+	}
+	PACK_OP_EXPORT_REPLY(0, &rep);
+
+	rc = usbip_net_send(sockfd, &reply, sizeof(reply));
+	if (rc < 0) {
+		dbg("usbip_net_send failed: export reply");
+		return -1;
+	}
+
+	rc = usbip_vhci_create_record(host, port, req.udev.busid, rhport);
+	if (rc < 0) {
+		err("record connection");
+		return -1;
+	}
+
+	dbg("export request busid %s: complete", req.udev.busid);
+
+	return 0;
+}
+
+static int unimport_device(char *host, struct usbip_usb_device *udev)
+{
+	int rc;
+	struct usbip_imported_device *idev;
+
+	idev = usbip_vhci_find_device(host, udev->busid);
+	if (idev == NULL) {
+		err("no imported port %s %s", host, udev->busid);
+		return -1;
+	}
+
+	rc = usbip_vhci_detach_device(idev->port);
+	if (rc < 0) {
+		err("no imported port %d %s %s", idev->port, host, udev->busid);
+		return -1;
+	}
+	return idev->port;
+}
+
+static int recv_request_unexport(int sockfd, char *host)
+{
+	struct op_unexport_request req;
+	struct op_unexport_reply reply;
+	int rhport = 0;
+	int error = 0;
+	int rc;
+
+	memset(&req, 0, sizeof(req));
+	memset(&reply, 0, sizeof(reply));
+
+	rc = usbip_net_recv(sockfd, &req, sizeof(req));
+	if (rc < 0) {
+		dbg("usbip_net_recv failed: unexport request");
+		return -1;
+	}
+	PACK_OP_UNEXPORT_REQUEST(0, &req);
+
+	rhport = unimport_device(host, &req.udev);
+	if (rhport < 0) {
+		error = 1;
+	}
+
+	rc = usbip_net_send_op_common(sockfd, OP_REP_UNEXPORT,
+				      (!error ? ST_OK : ST_NA));
+	if (rc < 0) {
+		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_UNEXPORT);
+		return -1;
+	}
+
+	if (!error) {
+		reply.returncode = 0;
+	} else {
+		reply.returncode = -1;
+		dbg("unexport request busid %s: failed", req.udev.busid);
+		return -1;
+	}
+	PACK_OP_UNEXPORT_REPLY(0, &rep);
+
+	rc = usbip_net_send(sockfd, &reply, sizeof(reply));
+	if (rc < 0) {
+		dbg("usbip_net_send failed: unexport reply");
+		return -1;
+	}
+
+	usbip_vhci_delete_record(rhport);
+
+	dbg("unexport request busid %s: complete", req.udev.busid);
+
+	return 0;
+}
+
+int usbip_recv_pdu(int connfd, char *host, char *port)
+{
+	uint16_t code = OP_UNSPEC;
+	int ret;
+
+	ret = usbip_net_recv_op_common(connfd, &code);
+	if (ret < 0) {
+		dbg("could not receive opcode: %#0x", code);
+		return -1;
+	}
+
+	ret = usbip_vhci_refresh_device_list();
+	if (ret < 0) {
+		dbg("could not refresh device list: %d", ret);
+		return -1;
+	}
+
+	info("received request: %#0x(%d)", code, connfd);
+	switch (code) {
+	case OP_REQ_EXPORT:
+		ret = recv_request_export(connfd, host, port);
+		break;
+	case OP_REQ_UNEXPORT:
+		ret = recv_request_unexport(connfd, host);
+		break;
+	default:
+		err("received an unknown opcode: %#0x", code);
+		ret = -1;
+	}
+
+	if (ret == 0)
+		info("request %#0x(%s:%s): complete", code, host, port);
+	else
+		info("request %#0x(%s:%s): failed", code, host, port);
+
+	return ret;
+}
+
diff --git a/tools/usb/usbip/src/usbipd_dev.c b/tools/usb/usbip/src/usbipd_dev.c
new file mode 100644
index 0000000..7991e3e
--- /dev/null
+++ b/tools/usb/usbip/src/usbipd_dev.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2015 Nobuo Iwata
+ *               2011 matt mooney <mfm@xxxxxxxxxxxxx>
+ *               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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "usbip_host_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "list.h"
+
+char *usbip_progname = "usbipd";
+char *usbip_default_pid_file = "/var/run/usbipd";
+
+int usbip_driver_open(void)
+{
+	if (usbip_host_driver_open()) {
+		err("please load " USBIP_CORE_MOD_NAME ".ko and "
+			USBIP_HOST_DRV_NAME ".ko!");
+		return -1;
+	}
+	return 0;
+}
+
+void usbip_driver_close(void)
+{
+	usbip_host_driver_close();
+}
+
+static int recv_request_import(int sockfd)
+{
+	struct op_import_request req;
+	struct op_common reply;
+	struct usbip_exported_device *edev;
+	struct usbip_usb_device pdu_udev;
+	struct list_head *i;
+	int found = 0;
+	int error = 0;
+	int rc;
+
+	memset(&req, 0, sizeof(req));
+	memset(&reply, 0, sizeof(reply));
+
+	rc = usbip_net_recv(sockfd, &req, sizeof(req));
+	if (rc < 0) {
+		dbg("usbip_net_recv failed: import request");
+		return -1;
+	}
+	PACK_OP_IMPORT_REQUEST(0, &req);
+
+	list_for_each(i, &host_driver->edev_list) {
+		edev = list_entry(i, struct usbip_exported_device, node);
+		if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
+			info("found requested device: %s", req.busid);
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		/* should set TCP_NODELAY for usbip */
+		usbip_net_set_nodelay(sockfd);
+
+		/* export device needs a TCP/IP socket descriptor */
+		rc = usbip_host_export_device(edev, sockfd);
+		if (rc < 0)
+			error = 1;
+	} else {
+		info("requested device not found: %s", req.busid);
+		error = 1;
+	}
+
+	rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
+				      (!error ? ST_OK : ST_NA));
+	if (rc < 0) {
+		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
+		return -1;
+	}
+
+	if (error) {
+		dbg("import request busid %s: failed", req.busid);
+		return -1;
+	}
+
+	memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+	usbip_net_pack_usb_device(1, &pdu_udev);
+
+	rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
+	if (rc < 0) {
+		dbg("usbip_net_send failed: devinfo");
+		return -1;
+	}
+
+	dbg("import request busid %s: complete", req.busid);
+
+	return 0;
+}
+
+static int send_reply_devlist(int connfd)
+{
+	struct usbip_exported_device *edev;
+	struct usbip_usb_device pdu_udev;
+	struct usbip_usb_interface pdu_uinf;
+	struct op_devlist_reply reply;
+	struct list_head *j;
+	int rc, i;
+
+	reply.ndev = 0;
+	/* number of exported devices */
+	list_for_each(j, &host_driver->edev_list) {
+		reply.ndev += 1;
+	}
+	info("exportable devices: %d", reply.ndev);
+
+	rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
+	if (rc < 0) {
+		dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
+		return -1;
+	}
+	PACK_OP_DEVLIST_REPLY(1, &reply);
+
+	rc = usbip_net_send(connfd, &reply, sizeof(reply));
+	if (rc < 0) {
+		dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
+		return -1;
+	}
+
+	list_for_each(j, &host_driver->edev_list) {
+		edev = list_entry(j, struct usbip_exported_device, node);
+		dump_usb_device(&edev->udev);
+		memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+		usbip_net_pack_usb_device(1, &pdu_udev);
+
+		rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
+		if (rc < 0) {
+			dbg("usbip_net_send failed: pdu_udev");
+			return -1;
+		}
+
+		for (i = 0; i < edev->udev.bNumInterfaces; i++) {
+			dump_usb_interface(&edev->uinf[i]);
+			memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
+			usbip_net_pack_usb_interface(1, &pdu_uinf);
+
+			rc = usbip_net_send(connfd, &pdu_uinf,
+					sizeof(pdu_uinf));
+			if (rc < 0) {
+				err("usbip_net_send failed: pdu_uinf");
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int recv_request_devlist(int connfd)
+{
+	struct op_devlist_request req;
+	int rc;
+
+	memset(&req, 0, sizeof(req));
+
+	rc = usbip_net_recv(connfd, &req, sizeof(req));
+	if (rc < 0) {
+		dbg("usbip_net_recv failed: devlist request");
+		return -1;
+	}
+
+	rc = send_reply_devlist(connfd);
+	if (rc < 0) {
+		dbg("send_reply_devlist failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+int usbip_recv_pdu(int connfd, char *host, char *port)
+{
+	uint16_t code = OP_UNSPEC;
+	int ret;
+
+	ret = usbip_net_recv_op_common(connfd, &code);
+	if (ret < 0) {
+		dbg("could not receive opcode: %#0x", code);
+		return -1;
+	}
+
+	ret = usbip_host_refresh_device_list();
+	if (ret < 0) {
+		dbg("could not refresh device list: %d", ret);
+		return -1;
+	}
+
+	info("received request: %#0x(%d)", code, connfd);
+	switch (code) {
+	case OP_REQ_DEVLIST:
+		ret = recv_request_devlist(connfd);
+		break;
+	case OP_REQ_IMPORT:
+		ret = recv_request_import(connfd);
+		break;
+	case OP_REQ_DEVINFO:
+	case OP_REQ_CRYPKEY:
+	default:
+		err("received an unknown opcode: %#0x", code);
+		ret = -1;
+	}
+
+	if (ret == 0)
+		info("request %#0x(%s:%s): complete", code, host, port);
+	else
+		info("request %#0x(%s:%s): failed", code, host, port);
+
+	return ret;
+}
+
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux