[PATCH 04/13] USB/IP: kernel module for userspace URBs transmission

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

 



Originally, USB/IP transmits requests and response PDUs for preparation to transfer URBs in user space, after the preparation, URBs are transmitted in kernel space.

To make easy to introduce application network protocols like SSL, WebSocket and so on, the driver, usbip_ux.ko, forwards URBs to USB/IP user space utilities. It's just like fuse driver for user space file system. 
Then, utilities transfer URBs in user space.

To do so, usbip_trx_ops makes send/receive functions pluggable. kernel_sendmsg() and kernel_recvmsg() for kernel mode transfer can be substituted by read/write methods to user space utilities.

In the absence of usbip_ux.ko, original kernel space transferring is valid. usbip_ux.ko replaces usbip_trx_ops in its init routine.

A) Original - kernel space URBs transfer

User            +-------+   1) import/export    +-------+
space           |uspipd,|<--------------------->|usbip, |
                |usbip  |                       |usbipa |
                +---+---+                       +---+---+
                    |                               |
        2)Set sockfd|                               |2)Set sockfd
          thru sysfs|                               |  thru sysfs
                    V                               V
Kernel          +-------+        4)URBs         +-------+
space           |usbip  |<--------------------->|vhci   |
                |host.ko|                       |hcd.ko |
                +-------+                       +-------+
                3)link to kernel trx_ops        3)link to kernel trx_ops

B) New - user space URBs transfer

User            +-------+    1)import/export    +-------+
space           |uspipd,|<--------------------->|usbip, |
            +---|usbip  |<--------------------->|usbipa |---+
2)Set sockfd|+--+-------+        6)URBs         +-------+--+|2)Set sockfd
  thru ioctl||      ^                              ^       ||  thru ioctl
  (right)   ||      |5)read/write      5)read/write|       ||  (left)
3)Set sockfd||      +-------+               +------+       ||3)Set Sockfd
  thru sysfs|+-----------+  | /dev/usbip-ux |  +-----------+|  thru sysfs
  (left)    V            V  V               V  V            V  (right)
Kernel  +-------+       +-------+       +-------+       +-------+
space   |usbip  |<----->|usbip  |       |usbip  |<----->|vhci   |
        |host.ko|5)send |ux.ko  |       |ux.ko  |5)send |hcd.ko |
        +-------+  recv +-------+       +-------+  recv +-------+
        4)link to user trx_ops          4)link to user trx_ops

Kernel module configuration for the driver will be shown as below.

	USB/IP support 					USBIP_CORE
	+-- USB/IP userspace URB transmission		USBIP_UX
	+-- VHCI hcd					USBIP_HCD
	+-- Debug messages for USB/IP			USBIP_DEBUG

To get diff include/uapi/linux/usbip_ux.h, I used modified dontdiff.txt.

Signed-off-by: Nobuo Iwata <nobuo.iwata@xxxxxxxxxxxxxxx>
---
 drivers/usb/usbip/Kconfig        |  10 +
 drivers/usb/usbip/Makefile       |   3 +
 drivers/usb/usbip/stub_dev.c     |  16 +-
 drivers/usb/usbip/stub_rx.c      |   3 +-
 drivers/usb/usbip/stub_tx.c      |   5 +-
 drivers/usb/usbip/usbip_common.c |  79 ++++-
 drivers/usb/usbip/usbip_common.h |  29 +-
 drivers/usb/usbip/usbip_ux.c     | 602 +++++++++++++++++++++++++++++++++++++++
 drivers/usb/usbip/usbip_ux.h     |  82 ++++++
 drivers/usb/usbip/vhci_hcd.c     |   9 +-
 drivers/usb/usbip/vhci_rx.c      |   3 +-
 drivers/usb/usbip/vhci_sysfs.c   |  40 +--
 drivers/usb/usbip/vhci_tx.c      |   6 +-
 include/uapi/linux/usbip_ux.h    |  39 +++
 14 files changed, 869 insertions(+), 57 deletions(-)
 create mode 100644 drivers/usb/usbip/usbip_ux.c
 create mode 100644 drivers/usb/usbip/usbip_ux.h
 create mode 100644 include/uapi/linux/usbip_ux.h

diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig
index bd99e9e..e847d06 100644
--- a/drivers/usb/usbip/Kconfig
+++ b/drivers/usb/usbip/Kconfig
@@ -14,6 +14,16 @@ config USBIP_CORE
 
 	  If unsure, say N.
 
+config USBIP_UX
+	tristate "USB/IP userspace URB transmission"
+	depends on USBIP_CORE
+	---help---
+	  This moves USB/IP URB transmission to userspace
+	  to apply SSL, WebSocket and etc.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbip-ux.
+
 config USBIP_VHCI_HCD
 	tristate "VHCI hcd"
 	depends on USBIP_CORE
diff --git a/drivers/usb/usbip/Makefile b/drivers/usb/usbip/Makefile
index 9ecd615..fd5c1a5 100644
--- a/drivers/usb/usbip/Makefile
+++ b/drivers/usb/usbip/Makefile
@@ -3,6 +3,9 @@ ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG
 obj-$(CONFIG_USBIP_CORE) += usbip-core.o
 usbip-core-y := usbip_common.o usbip_event.o
 
+obj-$(CONFIG_USBIP_UX) += usbip-ux.o
+usbip-ux-y := usbip_ux.o
+
 obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o
 vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
 
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
index a3ec49b..d8d3add 100644
--- a/drivers/usb/usbip/stub_dev.c
+++ b/drivers/usb/usbip/stub_dev.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -58,7 +59,6 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 {
 	struct stub_device *sdev = dev_get_drvdata(dev);
 	int sockfd = 0;
-	struct socket *socket;
 	int rv;
 
 	if (!sdev) {
@@ -71,8 +71,6 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 		return -EINVAL;
 
 	if (sockfd != -1) {
-		int err;
-
 		dev_info(dev, "stub up\n");
 
 		spin_lock_irq(&sdev->ud.lock);
@@ -82,12 +80,10 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
 			goto err;
 		}
 
-		socket = sockfd_lookup(sockfd, &err);
-		if (!socket)
+		rv = usbip_trx_ops->link(&sdev->ud, sockfd);
+		if (rv)
 			goto err;
 
-		sdev->ud.tcp_socket = socket;
-
 		spin_unlock_irq(&sdev->ud.lock);
 
 		sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
@@ -162,11 +158,7 @@ static void stub_shutdown_connection(struct usbip_device *ud)
 	 * sk_wait_data returned though stub_rx thread was already finished by
 	 * step 1?
 	 */
-	if (ud->tcp_socket) {
-		dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n",
-			ud->tcp_socket);
-		kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
-	}
+	usbip_trx_ops->unlink(ud);
 
 	/* 1. stop threads */
 	if (ud->tcp_rx) {
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index 00e475c..94902a4 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -544,7 +545,7 @@ static void stub_rx_pdu(struct usbip_device *ud)
 	memset(&pdu, 0, sizeof(pdu));
 
 	/* receive a pdu header */
-	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+	ret = usbip_recv(ud, &pdu, sizeof(pdu));
 	if (ret != sizeof(pdu)) {
 		dev_err(dev, "recv a header, %d\n", ret);
 		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index f19f321..adf5792 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -260,7 +261,7 @@ static int stub_send_ret_submit(struct stub_device *sdev)
 			iovnum++;
 		}
 
-		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
+		ret = usbip_trx_ops->sendmsg(&sdev->ud, &msg,
 						iov,  iovnum, txsize);
 		if (ret != txsize) {
 			dev_err(&sdev->interface->dev,
@@ -335,7 +336,7 @@ static int stub_send_ret_unlink(struct stub_device *sdev)
 		iov[0].iov_len  = sizeof(pdu_header);
 		txsize += sizeof(pdu_header);
 
-		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
+		ret = usbip_trx_ops->sendmsg(&sdev->ud, &msg, iov,
 				     1, txsize);
 		if (ret != txsize) {
 			dev_err(&sdev->interface->dev,
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index facaaf0..963d8db 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -322,7 +323,7 @@ void usbip_dump_header(struct usbip_header *pdu)
 EXPORT_SYMBOL_GPL(usbip_dump_header);
 
 /* Receive data over TCP/IP. */
-int usbip_recv(struct socket *sock, void *buf, int size)
+int usbip_recv(struct usbip_device *ud, void *buf, int size)
 {
 	int result;
 	struct msghdr msg;
@@ -335,26 +336,28 @@ int usbip_recv(struct socket *sock, void *buf, int size)
 
 	usbip_dbg_xmit("enter\n");
 
-	if (!sock || !buf || !size) {
-		pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
-		       size);
+	if ((!ud->tcp_socket && !ud->ux) || !buf || !size) {
+		pr_err("invalid arg, sock %p ux %p buff %p size %d\n",
+			ud->tcp_socket, ud->ux, buf, size);
 		return -EINVAL;
 	}
 
 	do {
-		sock->sk->sk_allocation = GFP_NOIO;
 		iov.iov_base    = buf;
 		iov.iov_len     = size;
-		msg.msg_name    = NULL;
-		msg.msg_namelen = 0;
-		msg.msg_control = NULL;
-		msg.msg_controllen = 0;
-		msg.msg_flags      = MSG_NOSIGNAL;
+		if (usbip_trx_ops->mode == USBIP_TRX_MODE_KERNEL) {
+			ud->tcp_socket->sk->sk_allocation = GFP_NOIO;
+			msg.msg_name    = NULL;
+			msg.msg_namelen = 0;
+			msg.msg_control = NULL;
+			msg.msg_controllen = 0;
+			msg.msg_flags      = MSG_NOSIGNAL;
+		}
 
-		result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
+		result = usbip_trx_ops->recvmsg(ud, &msg, &iov, 1, size, MSG_WAITALL);
 		if (result <= 0) {
-			pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
-				 sock, buf, size, result, total);
+			pr_debug("receive sock %p ux %p buf %p size %u ret %d total %d\n",
+				 ud->tcp_socket, ud->ux, buf, size, result, total);
 			goto err;
 		}
 
@@ -637,7 +640,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
 	if (!buff)
 		return -ENOMEM;
 
-	ret = usbip_recv(ud->tcp_socket, buff, size);
+	ret = usbip_recv(ud, buff, size);
 	if (ret != size) {
 		dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n",
 			ret);
@@ -741,7 +744,7 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
 	if (!(size > 0))
 		return 0;
 
-	ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
+	ret = usbip_recv(ud, urb->transfer_buffer, size);
 	if (ret != size) {
 		dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
 		if (ud->side == USBIP_STUB) {
@@ -756,6 +759,52 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
 }
 EXPORT_SYMBOL_GPL(usbip_recv_xbuff);
 
+static int usbip_kernel_sendmsg(struct usbip_device *udev,
+        struct msghdr *msg, struct kvec *vec, size_t num, size_t len)
+{
+        return kernel_sendmsg(udev->tcp_socket, msg, vec,  num, len);
+}
+
+static int usbip_kernel_recvmsg(struct usbip_device *udev,
+        struct msghdr *msg, struct kvec *vec, size_t num, size_t len, int flags)
+{
+        return kernel_recvmsg(udev->tcp_socket, msg, vec, num, len, flags);
+}
+
+int usbip_kernel_link(struct usbip_device *udev, int sockfd)
+{
+	int err;
+
+        udev->tcp_socket = sockfd_lookup(sockfd, &err);
+        if (udev->tcp_socket == NULL) {
+                pr_debug("Fail to link sock %d\n", sockfd);
+                return -EINVAL;
+        }
+        return 0;
+}
+EXPORT_SYMBOL_GPL(usbip_kernel_link);
+
+static int usbip_kernel_unlink(struct usbip_device *udev)
+{
+        if (!udev->tcp_socket) {
+                pr_debug("Fail to unlink sock %p\n", udev->tcp_socket);
+                return -EBADF;
+        }
+        pr_debug("shutting down tcp_socket %p\n", udev->tcp_socket);
+        return kernel_sock_shutdown(udev->tcp_socket, SHUT_RDWR);
+}
+
+static struct usbip_trx_operations usbip_trx_kernel_ops = {
+	.mode = USBIP_TRX_MODE_KERNEL,
+	.sendmsg = usbip_kernel_sendmsg,
+	.recvmsg = usbip_kernel_recvmsg,
+	.link = usbip_kernel_link,
+	.unlink = usbip_kernel_unlink,
+};
+
+struct usbip_trx_operations *usbip_trx_ops = &usbip_trx_kernel_ops;
+EXPORT_SYMBOL_GPL(usbip_trx_ops);
+
 static int __init usbip_core_init(void)
 {
 	pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h
index 86b0847..33b470c 100644
--- a/drivers/usb/usbip/usbip_common.h
+++ b/drivers/usb/usbip/usbip_common.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -30,6 +31,7 @@
 #include <linux/usb.h>
 #include <linux/wait.h>
 #include <uapi/linux/usbip.h>
+#include "usbip_ux.h"
 
 #define USBIP_VERSION "1.0.0"
 
@@ -56,7 +58,9 @@ enum {
 	usbip_debug_vhci_hc	= (1 << 9),
 	usbip_debug_vhci_rx	= (1 << 10),
 	usbip_debug_vhci_tx	= (1 << 11),
-	usbip_debug_vhci_sysfs  = (1 << 12)
+	usbip_debug_vhci_sysfs  = (1 << 12),
+
+	usbip_debug_ux		= (1 << 13)
 };
 
 #define usbip_dbg_flag_xmit	(usbip_debug_flag & usbip_debug_xmit)
@@ -67,6 +71,7 @@ enum {
 #define usbip_dbg_flag_stub_rx	(usbip_debug_flag & usbip_debug_stub_rx)
 #define usbip_dbg_flag_stub_tx	(usbip_debug_flag & usbip_debug_stub_tx)
 #define usbip_dbg_flag_vhci_sysfs  (usbip_debug_flag & usbip_debug_vhci_sysfs)
+#define usbip_dbg_flag_ux	(usbip_debug_flag & usbip_debug_ux)
 
 extern unsigned long usbip_debug_flag;
 extern struct device_attribute dev_attr_usbip_debug;
@@ -96,6 +101,8 @@ extern struct device_attribute dev_attr_usbip_debug;
 	usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args)
 #define usbip_dbg_vhci_sysfs(fmt, args...) \
 	usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args)
+#define usbip_dbg_ux(fmt, args...) \
+	usbip_dbg_with_flag(usbip_debug_ux, fmt , ##args)
 
 #define usbip_dbg_stub_cmp(fmt, args...) \
 	usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args)
@@ -262,6 +269,7 @@ struct usbip_device {
 	spinlock_t lock;
 
 	struct socket *tcp_socket;
+	usbip_ux_t *ux;
 
 	struct task_struct *tcp_rx;
 	struct task_struct *tcp_tx;
@@ -298,7 +306,7 @@ struct usbip_device {
 void usbip_dump_urb(struct urb *purb);
 void usbip_dump_header(struct usbip_header *pdu);
 
-int usbip_recv(struct socket *sock, void *buf, int size);
+int usbip_recv(struct usbip_device *ud, void *buf, int size);
 
 void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
 		    int pack);
@@ -332,4 +340,21 @@ static inline int interface_to_devnum(struct usb_interface *interface)
 	return udev->devnum;
 }
 
+#define USBIP_TRX_MODE_KERNEL	0
+#define USBIP_TRX_MODE_USER	1
+
+struct usbip_trx_operations {
+	int mode;
+	int (*sendmsg) (struct usbip_device *udev, struct msghdr *msg,
+			struct kvec *vec, size_t num, size_t len);
+	int (*recvmsg) (struct usbip_device *udev, struct msghdr *msg,
+			struct kvec *vec, size_t num, size_t len, int flags);
+	int (*link) (struct usbip_device *udev, int sockfd);
+	int (*unlink) (struct usbip_device *udev);
+};
+
+extern struct usbip_trx_operations *usbip_trx_ops;
+
+extern int usbip_kernel_link(struct usbip_device *udev, int sockfd);
+
 #endif /* __USBIP_COMMON_H */
diff --git a/drivers/usb/usbip/usbip_ux.c b/drivers/usb/usbip/usbip_ux.c
new file mode 100644
index 0000000..88da0c9
--- /dev/null
+++ b/drivers/usb/usbip/usbip_ux.c
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2015 Nobuo Iwata
+ *
+ * This 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * USB/IP userspace transmission module.
+ */
+
+#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/rcupdate.h>
+#include <linux/file.h>
+#include <asm/current.h>
+#include <asm/uaccess.h>
+
+#include <uapi/linux/usbip_ux.h>
+#include "usbip_common.h"
+
+#define DRIVER_AUTHOR "Nobuo Iwata <nobuo.iwata@xxxxxxxxxxxxxxx>"
+#define DRIVER_DESC "USB/IP Userspace Transfer"
+
+static DEFINE_SEMAPHORE(usbip_ux_lock);
+static struct list_head usbip_ux_list;
+
+static int usbip_ux_get(usbip_ux_t *ux)
+{
+	if (down_interruptible(&ux->lock)) return -ERESTARTSYS;
+	atomic_inc(&ux->count);
+	up(&ux->lock);
+	return 0;
+}
+
+static void usbip_ux_put(usbip_ux_t *ux)
+{
+	atomic_dec(&ux->count);
+}
+
+static int usbip_ux_is_linked(usbip_ux_t *ux)
+{
+	if (ux->ud) {
+		return 1;
+	}
+	return 0;
+}
+
+static void usbip_ux_wakeup_all(usbip_ux_t *ux)
+{
+	wake_up(&ux->tx_req_q);
+	wake_up(&ux->tx_rsp_q);
+	wake_up(&ux->rx_req_q);
+	wake_up(&ux->rx_rsp_q);
+}
+
+static ssize_t usbip_ux_read(struct file *file, char *buf, size_t blen, loff_t *off)
+{
+	usbip_ux_t *ux = (usbip_ux_t*)file->private_data;
+	int ret, bytes;
+	
+	usbip_dbg_ux("read waiting.\n");
+	ux->tx_error = 0;
+	ret = usbip_ux_get(ux);
+	if (ret) {
+		pr_err("Fail to get ux.\n");
+		goto err0;
+	}
+	if (!usbip_ux_is_linked(ux)) {
+		pr_info("Read from unlinked ux.\n");
+		if (USBIP_UX_IS_TX_INT(ux)) {
+			ret = -ERESTARTSYS;
+			goto err1;
+		}
+		ret = 0;
+		goto err1;
+	}
+	ret = wait_event_interruptible(ux->tx_req_q,
+		USBIP_UX_HAS_TX_REQ(ux) || USBIP_UX_IS_TX_INT(ux));
+	if (USBIP_UX_IS_TX_INT(ux)) {
+		ret = -ERESTARTSYS;
+	}
+	if (ret) {
+		ux->tx_error = ret;
+		USBIP_UX_CLEAR_TX_REQ(ux);
+		USBIP_UX_SET_TX_RSP(ux);
+		wake_up(&ux->tx_rsp_q);
+		goto err1;
+	}
+	bytes = ux->tx_bytes - ux->tx_count;
+	if (blen < bytes) bytes = blen;
+	usbip_dbg_ux("read copying %d.\n", bytes);
+	ret = copy_to_user(buf, ux->tx_buf + ux->tx_count, bytes);
+	if (ret > 0) {
+		ux->tx_error = -EIO;
+		USBIP_UX_CLEAR_TX_REQ(ux);
+		USBIP_UX_SET_TX_RSP(ux);
+		wake_up(&ux->tx_rsp_q);
+		ret = -EIO;
+		goto err1;
+	}
+	ux->tx_count += bytes;
+	if (ux->tx_count >= ux->tx_bytes) {
+		USBIP_UX_CLEAR_TX_REQ(ux);
+		USBIP_UX_SET_TX_RSP(ux);
+		wake_up(&ux->tx_rsp_q);
+	}
+	usbip_ux_put(ux);
+	return bytes;
+err1:
+	usbip_ux_put(ux);
+err0:
+	return ret;
+}
+
+static int usbip_ux_sendvec(usbip_ux_t *ux, struct kvec *vec)
+{
+	int ret;
+
+	ux->tx_buf = vec->iov_base;
+	ux->tx_bytes = vec->iov_len;
+	ux->tx_count = 0;
+	ux->tx_error = 0;
+	USBIP_UX_CLEAR_TX_RSP(ux);
+	USBIP_UX_SET_TX_REQ(ux);
+	wake_up(&ux->tx_req_q);
+	usbip_dbg_ux("sendvec waiting.\n");
+	ret = wait_event_interruptible(ux->tx_rsp_q,
+		USBIP_UX_HAS_TX_RSP(ux) || USBIP_UX_IS_TX_INT(ux));
+	if (ret) {
+		return ret;
+	} else if (USBIP_UX_IS_TX_INT(ux)) {
+		return -ERESTARTSYS;
+	}
+	return ux->tx_error;
+}
+
+int usbip_ux_sendmsg(struct usbip_device *ud, struct msghdr *msg, 
+		struct kvec *vec, size_t num, size_t size)
+{
+	int ret, i, count = 0;
+	usbip_ux_t *ux;
+
+	usbip_dbg_ux("sendmsg.\n");
+	rcu_read_lock();
+	ux = rcu_dereference(ud->ux);
+	if (ux == NULL) {
+		pr_info("Send to unlinked ux (0).\n");
+		ret = 0;
+		goto err1;
+	}
+	ret = usbip_ux_get(ux);
+	if (ret) {
+		pr_err("Fail to get ux.\n");
+		goto err1;
+	} else if (!usbip_ux_is_linked(ux)) {
+		pr_info("Send to unlinked ux (1).\n");
+		ret = 0;
+		goto err2;
+	}
+	for(i = 0;i < num;i++) {
+		ret = usbip_ux_sendvec(ux, vec+i);
+		if (ret) {
+			pr_err("Fail to send by %d.\n", ret);
+			goto err2;
+		}
+		count += ux->tx_count;
+	}
+	usbip_ux_put(ux);
+	rcu_read_unlock();
+	usbip_dbg_ux("sendmsg. ok\n");
+	return count;
+err2:
+	usbip_ux_put(ux);
+err1:
+	rcu_read_unlock();
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usbip_ux_sendmsg);
+
+static ssize_t usbip_ux_write(struct file *file, const char *buf, size_t blen, loff_t *off)
+{
+	usbip_ux_t *ux = (usbip_ux_t*)file->private_data;
+	int ret, bytes;
+
+	usbip_dbg_ux("write waiting.\n");
+	ret = usbip_ux_get(ux);
+	if (ret) {
+		pr_err("Fail to get ux.\n");
+		goto err0;
+	} else if (!usbip_ux_is_linked(ux)) {
+		pr_info("Write to unlinked ux.\n");
+		ret = -EINTR;
+		goto err1;
+	}
+	ux->rx_error = 0;
+	ret = wait_event_interruptible(ux->rx_req_q,
+		USBIP_UX_HAS_RX_REQ(ux) || USBIP_UX_IS_RX_INT(ux));
+	if (USBIP_UX_IS_RX_INT(ux)) {
+		ret = -ERESTARTSYS;
+	}
+	if (ret) {
+		ux->rx_error = ret;
+		USBIP_UX_CLEAR_RX_REQ(ux);
+		USBIP_UX_SET_RX_RSP(ux);
+		wake_up(&ux->rx_rsp_q);
+		goto err1;
+	}
+	bytes = ux->rx_bytes - ux->rx_count;
+	if (blen < bytes) bytes = blen;
+	usbip_dbg_ux("write copying %d.\n", bytes);
+	ret = copy_from_user(ux->rx_buf + ux->rx_count, buf, bytes);
+	if (ret > 0) {
+		ux->rx_error = -EIO;
+		USBIP_UX_CLEAR_RX_REQ(ux);
+		USBIP_UX_SET_RX_RSP(ux);
+		wake_up(&ux->rx_rsp_q);
+		ret = -EIO;
+		goto err1;
+	}
+	ux->rx_count += bytes;
+	USBIP_UX_CLEAR_RX_REQ(ux);
+	USBIP_UX_SET_RX_RSP(ux);
+	wake_up(&ux->rx_rsp_q);
+	usbip_ux_put(ux);
+	return bytes;
+err1:
+	usbip_ux_put(ux);
+err0:
+	return ret;
+}
+
+static int usbip_ux_recvvec(usbip_ux_t *ux, struct kvec *vec)
+{
+	int ret;
+
+	ux->rx_buf = vec->iov_base;
+	ux->rx_bytes = vec->iov_len;
+	ux->rx_count = 0;
+	ux->rx_error = 0;
+	USBIP_UX_CLEAR_RX_RSP(ux);
+	USBIP_UX_SET_RX_REQ(ux);
+	wake_up(&ux->rx_req_q);
+	usbip_dbg_ux("recvvec waiting.\n");
+	ret = wait_event_interruptible(ux->rx_rsp_q,
+		USBIP_UX_HAS_RX_RSP(ux) || USBIP_UX_IS_RX_INT(ux));
+	if (ret) {
+		return ret;
+	} else if (USBIP_UX_IS_RX_INT(ux)) {
+		usbip_dbg_ux("interrupted.\n");
+		return -ERESTARTSYS;
+	}
+	return ux->rx_error;
+}
+
+int usbip_ux_recvmsg(struct usbip_device *ud, struct msghdr *msg,
+		struct kvec *vec, size_t num, size_t size, int flags)
+{
+	int ret, i, count = 0;
+	usbip_ux_t *ux;
+
+	usbip_dbg_ux("recvmsg.\n");
+	rcu_read_lock();
+	ux = rcu_dereference(ud->ux);
+	if (ux == NULL) {
+		pr_err("Recv from unlinked ux (0).\n");
+		ret = 0;
+		goto err1;
+	}
+	ret = usbip_ux_get(ux);
+	if (ret) {
+		pr_err("Fail to get ux.\n");
+		goto err1;
+	} else if (!usbip_ux_is_linked(ux)) {
+		pr_err("Recv from unlinked ux (1).\n");
+		ret = 0;
+		goto err2;
+	}
+	for(i = 0;i < num;i++) {
+		usbip_dbg_ux("recvmsg. %d\n", i);
+		ret = usbip_ux_recvvec(ux, vec+i);
+		if (ret) {
+			pr_err("Fail to recv by %d.\n", ret);
+			goto err2;
+		}
+		usbip_dbg_ux("recvmsg ok. %d\n", i);
+		count += ux->rx_count;
+	}
+	usbip_ux_put(ux);
+	rcu_read_unlock();
+	usbip_dbg_ux("recvmsg ok.");
+	return count;
+err2:
+	usbip_ux_put(ux);
+err1:
+	rcu_read_unlock();
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usbip_ux_recvmsg);
+
+static int usbip_ux_new(usbip_ux_t **uxp)
+{
+	usbip_ux_t *ux;
+
+	ux = (usbip_ux_t*)kzalloc(sizeof(usbip_ux_t), GFP_KERNEL);
+	if (!ux) {
+		pr_err("Fail to alloc usbip_ux_t.\n");
+		return -ENOMEM;
+	}
+	sema_init(&ux->lock, 1);
+	atomic_set(&ux->count, 0);
+	init_waitqueue_head(&ux->tx_req_q);
+	init_waitqueue_head(&ux->tx_rsp_q);
+	init_waitqueue_head(&ux->rx_req_q);
+	init_waitqueue_head(&ux->rx_rsp_q);
+	ux->pgid = task_pgrp_vnr(current);
+	if (down_interruptible(&usbip_ux_lock)) return -ERESTARTSYS;
+	list_add_tail(&ux->node, &usbip_ux_list);
+	up(&usbip_ux_lock);
+	*uxp = ux;
+	return 0;
+}
+
+static int usbip_ux_delete(usbip_ux_t *ux)
+{
+	USBIP_UX_SET_INT(ux);
+	usbip_ux_wakeup_all(ux);
+	if (down_interruptible(&usbip_ux_lock)) return -ERESTARTSYS;
+	if (down_interruptible(&ux->lock)) {
+		up(&usbip_ux_lock);
+		return -ERESTARTSYS;
+	}
+	pr_info("Waiting ux becomes free in delete.");
+	while(atomic_read(&ux->count) > 0) yield();
+	pr_info("End of waiting ux becomes free in delete.");
+	list_del(&ux->node);
+	if (ux->ud != NULL) {
+		rcu_assign_pointer(ux->ud->ux, NULL);
+		ux->ud = NULL;
+	}
+	up(&ux->lock);
+	up(&usbip_ux_lock);
+	synchronize_rcu();
+	pr_info("Releasing ux %p.\n", ux);
+	kfree(ux);
+	return 0;
+}
+
+int usbip_ux_link(struct usbip_device *ud, int sockfd)
+{
+	usbip_ux_t *ux;
+	struct list_head *p;
+	int ret;
+
+	usbip_dbg_ux("linking ud:%p sock:%d\n", ud, sockfd);
+	ret = usbip_kernel_link(ud, sockfd);
+	if (ret) {
+		return ret;
+	}
+	if (down_interruptible(&usbip_ux_lock)) return -ERESTARTSYS;
+	list_for_each(p, &usbip_ux_list) {
+		ux = list_entry(p, usbip_ux_t, node);
+		if (ux->tcp_socket == ud->tcp_socket) {
+			ud->ux = ux;
+			ux->ud = ud;
+			up(&usbip_ux_lock);
+			usbip_dbg_ux("linked ud:%p sock:%d ux:%p\n", ud, sockfd, ux);
+			return 0;
+		}
+	}
+	up(&usbip_ux_lock);
+	usbip_dbg_ux("fail to link ud:%p sock:%d\n", ud, sockfd);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usbip_ux_link);
+
+int usbip_ux_unlink(struct usbip_device *ud)
+{
+	usbip_ux_t *ux;
+
+	usbip_dbg_ux("unlinking ux:%p\n", ud->ux);
+	rcu_read_lock();
+	ux = rcu_dereference(ud->ux);
+	if (ux == NULL) {
+		pr_err("Unlink to unlinked ux.\n");
+		rcu_read_unlock();
+		return -EINVAL;
+	}
+	pr_info("Unlink ux sock:%d.\n", ux->sockfd);
+	USBIP_UX_SET_INT(ux);
+	usbip_ux_wakeup_all(ux);
+	if (down_interruptible(&ux->lock)) {
+		rcu_read_unlock();
+		return -ERESTARTSYS;
+	}
+	rcu_read_unlock();
+	pr_info("Waiting ux becomes free in unlink.");
+	while(atomic_read(&ux->count) > 0) yield();
+	pr_info("End of waiting ux becomes free in unlink.");
+	rcu_assign_pointer(ud->ux, NULL);
+	ux->ud = NULL;
+	up(&ux->lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usbip_ux_unlink);
+
+static int usbip_ux_set_sockfd(usbip_ux_t *ux, int sockfd)
+{
+	int err;
+
+	if (ux->sockfd != 0) {
+		return -EBUSY;
+	}
+	ux->tcp_socket = sockfd_lookup(sockfd, &err);
+	if (ux->tcp_socket == NULL) {
+		pr_debug("Fail to sock ptr fd:%d pid:%d\n", sockfd, task_pid_nr(current));
+		return -EINVAL;
+	}
+	fput(ux->tcp_socket->file);
+	ux->sockfd = sockfd;
+	return 0;
+}
+
+static int usbip_ux_interrupt(usbip_ux_t *ux)
+{
+	usbip_dbg_ux("interrupt %p %d\n", ux, ux->sockfd);
+	USBIP_UX_SET_INT(ux);
+	usbip_ux_wakeup_all(ux);
+	return 0;
+}
+
+static int usbip_ux_interrupt_pgrp(void)
+{
+	pid_t pgid;
+	struct list_head *p;
+	usbip_ux_t *ux;
+
+	pgid = task_pgrp_vnr(current);
+	if (down_interruptible(&usbip_ux_lock)) return -ERESTARTSYS;
+	list_for_each(p, &usbip_ux_list) {
+		ux = list_entry(p, usbip_ux_t, node);
+		if (ux->pgid == pgid) {
+			usbip_ux_interrupt(ux);
+		}
+	}
+	up(&usbip_ux_lock);
+	return 0;
+}
+
+static int usbip_ux_getkaddr(usbip_ux_t *ux, void __user *ubuf)
+{
+	struct usbip_ux_kaddr kaddr = {(void*)ux, (void*)(ux->tcp_socket)};
+	if (copy_to_user(ubuf, &kaddr, sizeof(kaddr))) {
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static long usbip_ux_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	usbip_ux_t *ux = (usbip_ux_t*)file->private_data;
+
+	if (((_IOC_DIR(cmd) & _IOC_READ) &&
+		!access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd))) ||
+		((_IOC_DIR(cmd) & _IOC_WRITE) &&
+		!access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)))) {
+		return -EFAULT;
+	}
+	switch (_IOC_NR(cmd)) {
+	case _IOC_NR(USBIP_UX_IOCSETSOCKFD):
+		return usbip_ux_set_sockfd(ux, (int)arg);
+	case _IOC_NR(USBIP_UX_IOCINTR):
+		return usbip_ux_interrupt(ux);
+	case _IOC_NR(USBIP_UX_IOCINTRPGRP):
+		return usbip_ux_interrupt_pgrp();
+	case _IOC_NR(USBIP_UX_IOCGETKADDR):
+		return usbip_ux_getkaddr(ux, (void __user *)arg);
+	}
+	return -EINVAL;
+}
+
+static int usbip_ux_open(struct inode *inode, struct file *file)
+{
+	usbip_ux_t *ux = NULL;
+	int ret;
+
+	ret = usbip_ux_new(&ux);
+	if (ux == NULL) {
+		pr_err("Fail to alloc usbip_ux_t.\n");
+		return -ENOMEM;
+	}
+	file->private_data = ux;
+	return 0;
+}
+
+static int usbip_ux_release(struct inode *inode, struct file *file)
+{
+	usbip_ux_t *ux = file->private_data;
+
+	return usbip_ux_delete(ux);
+}
+
+static struct usbip_trx_operations usbip_trx_user_ops = {
+	.mode = USBIP_TRX_MODE_USER,
+	.sendmsg = usbip_ux_sendmsg,
+	.recvmsg = usbip_ux_recvmsg,
+	.link = usbip_ux_link,
+	.unlink = usbip_ux_unlink,
+};
+
+static struct file_operations usbip_ux_fops = {
+	.owner = THIS_MODULE,
+	.read = usbip_ux_read,
+	.write = usbip_ux_write,
+	.unlocked_ioctl = usbip_ux_ioctl,
+	.compat_ioctl = usbip_ux_ioctl,
+	.open = usbip_ux_open,
+	.release = usbip_ux_release,
+};
+
+static dev_t usbip_ux_devno;
+static struct cdev usbip_ux_cdev;
+static struct class *usbip_ux_class;
+static struct device *usbip_ux_dev;
+static struct usbip_trx_operations *usbip_trx_ops_bak;
+
+static int __init usbip_ux_init(void)
+{
+	int ret;
+
+	usbip_trx_ops_bak = usbip_trx_ops;
+	usbip_trx_ops = &usbip_trx_user_ops;
+
+	INIT_LIST_HEAD(&usbip_ux_list);
+	ret = alloc_chrdev_region(&usbip_ux_devno, USBIP_UX_MINOR, 1, USBIP_UX_DEV_NAME);
+	if (ret < 0) {
+		pr_err("Fail to alloc chrdev for %s\n", USBIP_UX_DEV_NAME);
+		goto err0;
+	}
+	cdev_init(&usbip_ux_cdev, &usbip_ux_fops);
+	usbip_ux_cdev.owner = usbip_ux_fops.owner;
+	ret = cdev_add(&usbip_ux_cdev, usbip_ux_devno, 1);
+	if (ret < 0) {
+		pr_err("Fail to add cdev: %s\n", USBIP_UX_DEV_NAME);
+		kobject_put(&usbip_ux_cdev.kobj);
+		goto err1;
+	}
+	usbip_ux_class = class_create(THIS_MODULE, USBIP_UX_CLASS_NAME);
+	if (IS_ERR(usbip_ux_class)) {
+		pr_err("Fail to create class: %s\n", USBIP_UX_CLASS_NAME);
+		ret = PTR_ERR(usbip_ux_class);
+		goto err2;
+	}
+	usbip_ux_dev = device_create(usbip_ux_class, NULL, usbip_ux_devno, NULL, USBIP_UX_DEV_NAME);
+	if (IS_ERR(usbip_ux_dev)) {
+		pr_err("Fail to create sysfs entry for %s\n", USBIP_UX_DEV_NAME);
+		ret = PTR_ERR(usbip_ux_class);
+		goto err2;
+	}
+	return 0;
+err2:
+	cdev_del(&usbip_ux_cdev);
+err1:
+	unregister_chrdev_region(usbip_ux_devno, 1);
+err0:
+	return ret;
+}
+
+static void __exit usbip_ux_exit(void)
+{
+	device_destroy(usbip_ux_class, usbip_ux_devno);
+	class_destroy(usbip_ux_class);
+	cdev_del(&usbip_ux_cdev);
+	unregister_chrdev_region(usbip_ux_devno, 1);
+	usbip_trx_ops = usbip_trx_ops_bak;
+}
+
+module_init(usbip_ux_init);
+module_exit(usbip_ux_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(USBIP_VERSION);
+
diff --git a/drivers/usb/usbip/usbip_ux.h b/drivers/usb/usbip/usbip_ux.h
new file mode 100644
index 0000000..515245d
--- /dev/null
+++ b/drivers/usb/usbip/usbip_ux.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 Nobuo Iwata
+ *
+ * This 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __USBIP_UX_H
+#define __USBIP_UX_H
+
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <asm/atomic.h>
+
+#define USBIP_UX_INT		(int)3
+#define USBIP_UX_TX_INT		(int)1
+#define USBIP_UX_RX_INT		(int)2
+
+#define USBIP_UX_SET_INT(ux)	((ux)->interrupted = USBIP_UX_INT)
+#define USBIP_UX_SET_TX_INT(ux)	((ux)->interrupted |= USBIP_UX_TX_INT)
+#define USBIP_UX_SET_RX_INT(ux)	((ux)->interrupted |= USBIP_UX_RX_INT)
+#define USBIP_UX_IS_INT(ux)	((ux)->interrupted & USBIP_UX_INT)
+#define USBIP_UX_IS_TX_INT(ux)	((ux)->interrupted & USBIP_UX_TX_INT)
+#define USBIP_UX_IS_RX_INT(ux)	((ux)->interrupted & USBIP_UX_RX_INT)
+
+#define USBIP_UX_REQ	(int)1
+#define USBIP_UX_RSP	(int)2
+#define USBIP_UX_SET_TX_REQ(ux)		((ux)->tx_flags |= USBIP_UX_REQ)
+#define USBIP_UX_SET_TX_RSP(ux)		((ux)->tx_flags |= USBIP_UX_RSP)
+#define USBIP_UX_SET_RX_REQ(ux)		((ux)->rx_flags |= USBIP_UX_REQ)
+#define USBIP_UX_SET_RX_RSP(ux)		((ux)->rx_flags |= USBIP_UX_RSP)
+#define USBIP_UX_CLEAR_TX_REQ(ux)	((ux)->tx_flags &= ~USBIP_UX_REQ)
+#define USBIP_UX_CLEAR_TX_RSP(ux)	((ux)->tx_flags &= ~USBIP_UX_RSP)
+#define USBIP_UX_CLEAR_RX_REQ(ux)	((ux)->rx_flags &= ~USBIP_UX_REQ)
+#define USBIP_UX_CLEAR_RX_RSP(ux)	((ux)->rx_flags &= ~USBIP_UX_RSP)
+#define USBIP_UX_HAS_TX_REQ(ux)		((ux)->tx_flags & USBIP_UX_REQ)
+#define USBIP_UX_HAS_TX_RSP(ux)		((ux)->tx_flags & USBIP_UX_RSP)
+#define USBIP_UX_HAS_RX_REQ(ux)		((ux)->rx_flags & USBIP_UX_REQ)
+#define USBIP_UX_HAS_RX_RSP(ux)		((ux)->rx_flags & USBIP_UX_RSP)
+
+typedef struct usbip_ux {
+	struct list_head node;
+	int sockfd;
+	struct socket *tcp_socket;
+	struct semaphore lock;
+	atomic_t count;
+	struct usbip_device *ud;
+	pid_t pgid;
+	int interrupted;
+	int tx_tout_sec;
+	int tx_flags;
+	int tx_error;
+	int tx_bytes;
+	int tx_count;
+	char *tx_buf;
+	wait_queue_head_t tx_req_q;
+	wait_queue_head_t tx_rsp_q;
+	int rx_flags;
+	int rx_error;
+	int rx_bytes;
+	int rx_count;
+	char *rx_buf;
+	wait_queue_head_t rx_req_q;
+	wait_queue_head_t rx_rsp_q;
+} usbip_ux_t;
+
+#endif /* __USBIP_UX_H */
+
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 11f6f61..852a2b3 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -769,11 +770,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
 {
 	struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
 
-	/* need this? see stub_dev.c */
-	if (ud->tcp_socket) {
-		pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket);
-		kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
-	}
+	usbip_trx_ops->unlink(ud);
 
 	/* kill threads related to this sdev */
 	if (vdev->ud.tcp_rx) {
@@ -790,6 +787,8 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
 	if (vdev->ud.tcp_socket) {
 		sockfd_put(vdev->ud.tcp_socket);
 		vdev->ud.tcp_socket = NULL;
+	} else if (ud->ux) {
+		ud->ux = NULL;
 	}
 	pr_info("release socket\n");
 
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index 00e4a54..4300bf1 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -205,7 +206,7 @@ static void vhci_rx_pdu(struct usbip_device *ud)
 	memset(&pdu, 0, sizeof(pdu));
 
 	/* receive a pdu header */
-	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+	ret = usbip_recv(ud, &pdu, sizeof(pdu));
 	if (ret < 0) {
 		if (ret == -ECONNRESET)
 			pr_info("connection reset by peer\n");
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index 211f43f..f04f15d 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -39,16 +40,17 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr,
 
 	/*
 	 * output example:
-	 * prt sta spd dev socket           local_busid
-	 * 000 004 000 000         c5a7bb80 1-2.3
-	 * 001 004 000 000         d8cee980 2-3.4
+	 * prt sta spd dev      socket           ux               local_busid
+	 * 000 004 000 00010002         c5a7bb80                0 1-2.3
+	 * 001 004 000 00020003         d8cee980                0 2-3.4
 	 *
 	 * IP address can be retrieved from a socket pointer address by looking
 	 * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
 	 * port number and its peer IP address.
 	 */
 	out += sprintf(out,
-		       "prt sta spd bus dev socket           local_busid\n");
+		       "prt sta spd dev      socket           "
+		       "ux               local_busid\n");
 
 	for (i = 0; i < VHCI_NPORTS; i++) {
 		struct vhci_device *vdev = port_to_vdev(i);
@@ -59,11 +61,19 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr,
 		if (vdev->ud.status == VDEV_ST_USED) {
 			out += sprintf(out, "%03u %08x ",
 				       vdev->speed, vdev->devid);
-			out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
+			if (vdev->ud.tcp_socket)
+				out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
+			else
+				out += sprintf(out, "%016x ", 0);
+			if (vdev->ud.ux)
+				out += sprintf(out, "%16p ", vdev->ud.ux);
+			else
+				out += sprintf(out, "%016x ", 0);
 			out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
 
 		} else {
-			out += sprintf(out, "000 000 000 0000000000000000 0-0");
+			out += sprintf(out, "000 00000000 0000000000000000 "
+					    "0000000000000000 0-0");
 		}
 
 		out += sprintf(out, "\n");
@@ -173,10 +183,9 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 			    const char *buf, size_t count)
 {
 	struct vhci_device *vdev;
-	struct socket *socket;
 	int sockfd = 0;
 	__u32 rhport = 0, devid = 0, speed = 0;
-	int err;
+	int ret;
 
 	/*
 	 * @rhport: port number of vhci_hcd
@@ -194,11 +203,6 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 	if (valid_args(rhport, speed) < 0)
 		return -EINVAL;
 
-	/* Extract socket from fd. */
-	socket = sockfd_lookup(sockfd, &err);
-	if (!socket)
-		return -EINVAL;
-
 	/* now need lock until setting vdev status as used */
 
 	/* begin a lock */
@@ -211,19 +215,23 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
 		spin_unlock(&vdev->ud.lock);
 		spin_unlock(&the_controller->lock);
 
-		sockfd_put(socket);
-
 		dev_err(dev, "port %d already used\n", rhport);
 		return -EINVAL;
 	}
 
+	ret = usbip_trx_ops->link(&vdev->ud, sockfd);
+	if (ret) {
+		spin_unlock(&vdev->ud.lock);
+		spin_unlock(&the_controller->lock);
+		return ret;
+	}
+
 	dev_info(dev,
 		 "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n",
 		 rhport, sockfd, devid, speed, usb_speed_string(speed));
 
 	vdev->devid         = devid;
 	vdev->speed         = speed;
-	vdev->ud.tcp_socket = socket;
 	vdev->ud.status     = VDEV_ST_NOTASSIGNED;
 
 	spin_unlock(&vdev->ud.lock);
diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c
index 409fd99..9c40a30 100644
--- a/drivers/usb/usbip/vhci_tx.c
+++ b/drivers/usb/usbip/vhci_tx.c
@@ -115,7 +115,7 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
 			txsize += len;
 		}
 
-		ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
+		ret = usbip_trx_ops->sendmsg(&vdev->ud, &msg, iov, 3, txsize);
 		if (ret != txsize) {
 			pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
 			       txsize);
@@ -155,7 +155,7 @@ static int vhci_send_cmd_unlink(struct vhci_device *vdev)
 	struct vhci_unlink *unlink = NULL;
 
 	struct msghdr msg;
-	struct kvec iov[3];
+	struct kvec iov[1];
 	size_t txsize;
 
 	size_t total_size = 0;
@@ -184,7 +184,7 @@ static int vhci_send_cmd_unlink(struct vhci_device *vdev)
 		iov[0].iov_len  = sizeof(pdu_header);
 		txsize += sizeof(pdu_header);
 
-		ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize);
+		ret = usbip_trx_ops->sendmsg(&vdev->ud, &msg, iov, 1, txsize);
 		if (ret != txsize) {
 			pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
 			       txsize);
diff --git a/include/uapi/linux/usbip_ux.h b/include/uapi/linux/usbip_ux.h
new file mode 100644
index 0000000..64230ad
--- /dev/null
+++ b/include/uapi/linux/usbip_ux.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 Nobuo Iwata
+ *
+ * This 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __UAPI_LINUX_USBIP_UX_H
+#define __UAPI_LINUX_USBIP_UX_H
+
+#define USBIP_UX_MINOR		0
+#define USBIP_UX_CLASS_NAME	"usbip"
+#define USBIP_UX_DEV_NAME	"usbip-ux"
+
+struct usbip_ux_kaddr {
+	void *ux;
+	void *sock;
+};
+
+#define USBIP_UX_MAGIC_IOC 'm'
+#define USBIP_UX_IOCSETSOCKFD	_IOW(USBIP_UX_MAGIC_IOC, 1, int) 
+#define USBIP_UX_IOCINTR	_IO(USBIP_UX_MAGIC_IOC, 2) 
+#define USBIP_UX_IOCINTRPGRP	_IO(USBIP_UX_MAGIC_IOC, 3) 
+#define USBIP_UX_IOCGETKADDR	_IOR(USBIP_UX_MAGIC_IOC, 4, struct usbip_ux_kaddr) 
+
+#endif /* __UAPI_LINUX_USBIP_UX_H */
+
-- 
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