aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/usbip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/usbip')
-rw-r--r--drivers/staging/usbip/Kconfig58
-rw-r--r--drivers/staging/usbip/Makefile15
-rw-r--r--drivers/staging/usbip/README1
-rw-r--r--drivers/staging/usbip/stub.h47
-rw-r--r--drivers/staging/usbip/stub_dev.c391
-rw-r--r--drivers/staging/usbip/stub_main.c286
-rw-r--r--drivers/staging/usbip/stub_rx.c273
-rw-r--r--drivers/staging/usbip/stub_tx.c166
-rw-r--r--drivers/staging/usbip/uapi/usbip.h26
-rw-r--r--drivers/staging/usbip/usbip_common.c615
-rw-r--r--drivers/staging/usbip/usbip_common.h391
-rw-r--r--drivers/staging/usbip/usbip_event.c57
-rw-r--r--drivers/staging/usbip/usbip_protocol.txt358
-rw-r--r--drivers/staging/usbip/userspace/.gitignore28
-rw-r--r--drivers/staging/usbip/userspace/AUTHORS3
-rw-r--r--drivers/staging/usbip/userspace/COPYING340
-rw-r--r--drivers/staging/usbip/userspace/INSTALL237
-rw-r--r--drivers/staging/usbip/userspace/Makefile.am6
-rw-r--r--drivers/staging/usbip/userspace/README202
-rwxr-xr-xdrivers/staging/usbip/userspace/autogen.sh9
-rwxr-xr-xdrivers/staging/usbip/userspace/cleanup.sh12
-rw-r--r--drivers/staging/usbip/userspace/configure.ac111
-rw-r--r--drivers/staging/usbip/userspace/doc/usbip.895
-rw-r--r--drivers/staging/usbip/userspace/doc/usbipd.891
-rw-r--r--drivers/staging/usbip/userspace/libsrc/Makefile.am8
-rw-r--r--drivers/staging/usbip/userspace/libsrc/list.h136
-rw-r--r--drivers/staging/usbip/userspace/libsrc/names.c504
-rw-r--r--drivers/staging/usbip/userspace/libsrc/names.h41
-rw-r--r--drivers/staging/usbip/userspace/libsrc/sysfs_utils.c31
-rw-r--r--drivers/staging/usbip/userspace/libsrc/sysfs_utils.h8
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_common.c285
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_common.h137
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c279
-rw-r--r--drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h49
-rw-r--r--drivers/staging/usbip/userspace/libsrc/vhci_driver.c411
-rw-r--r--drivers/staging/usbip/userspace/libsrc/vhci_driver.h59
-rw-r--r--drivers/staging/usbip/userspace/src/Makefile.am11
-rw-r--r--drivers/staging/usbip/userspace/src/usbip.c201
-rw-r--r--drivers/staging/usbip/userspace/src/usbip.h40
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_attach.c241
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_bind.c214
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_detach.c110
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_list.c283
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_network.c303
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_network.h185
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_port.c57
-rw-r--r--drivers/staging/usbip/userspace/src/usbip_unbind.c141
-rw-r--r--drivers/staging/usbip/userspace/src/usbipd.c679
-rw-r--r--drivers/staging/usbip/userspace/src/utils.c52
-rw-r--r--drivers/staging/usbip/userspace/src/utils.h25
-rw-r--r--drivers/staging/usbip/vhci.h63
-rw-r--r--drivers/staging/usbip/vhci_hcd.c666
-rw-r--r--drivers/staging/usbip/vhci_rx.c174
-rw-r--r--drivers/staging/usbip/vhci_sysfs.c69
-rw-r--r--drivers/staging/usbip/vhci_tx.c71
55 files changed, 7459 insertions, 1892 deletions
diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig
index b11ec379b5c..bd99e9e47e5 100644
--- a/drivers/staging/usbip/Kconfig
+++ b/drivers/staging/usbip/Kconfig
@@ -1,43 +1,41 @@
-config USB_IP_COMMON
- tristate "USB IP support (EXPERIMENTAL)"
- depends on USB && NET && EXPERIMENTAL && BKL
- default N
+config USBIP_CORE
+ tristate "USB/IP support"
+ depends on USB && NET
---help---
This enables pushing USB packets over IP to allow remote
- machines access to USB devices directly. For more details,
- and links to the userspace utility programs to let this work
- properly, see http://usbip.sourceforge.net/.
+ machines direct access to USB devices. It provides the
+ USB/IP core that is required by both drivers.
- To compile this driver as a module, choose M here: the
- module will be called usbip_common_mod.
+ For more details, and to get the userspace utility
+ programs, please see <http://usbip.sourceforge.net/>.
+
+ To compile this as a module, choose M here: the module will
+ be called usbip-core.
If unsure, say N.
-config USB_IP_VHCI_HCD
- tristate "USB IP client driver"
- depends on USB_IP_COMMON
- default N
+config USBIP_VHCI_HCD
+ tristate "VHCI hcd"
+ depends on USBIP_CORE
---help---
- This enables the USB IP host controller driver which will
- run on the client machine.
+ This enables the USB/IP virtual host controller driver,
+ which is run on the remote machine.
- To compile this driver as a module, choose M here: the
- module will be called vhci_hcd.
+ To compile this driver as a module, choose M here: the
+ module will be called vhci-hcd.
-config USB_IP_HOST
- tristate "USB IP host driver"
- depends on USB_IP_COMMON
- default N
+config USBIP_HOST
+ tristate "Host driver"
+ depends on USBIP_CORE
---help---
- This enables the USB IP device driver which will run on the
- host machine.
+ This enables the USB/IP host driver, which is run on the
+ machine that is sharing the USB devices.
- To compile this driver as a module, choose M here: the
- module will be called usbip.
+ To compile this driver as a module, choose M here: the
+ module will be called usbip-host.
-config USB_IP_DEBUG_ENABLE
- bool "USB-IP Debug Enable"
- depends on USB_IP_COMMON
- default N
+config USBIP_DEBUG
+ bool "Debug messages for USB/IP"
+ depends on USBIP_CORE
---help---
- This enables the debug messages from the USB-IP drivers.
+ This enables the debug messages from the USB/IP drivers.
diff --git a/drivers/staging/usbip/Makefile b/drivers/staging/usbip/Makefile
index 279f3cc3eea..9ecd61545be 100644
--- a/drivers/staging/usbip/Makefile
+++ b/drivers/staging/usbip/Makefile
@@ -1,11 +1,10 @@
-obj-$(CONFIG_USB_IP_COMMON) += usbip_common_mod.o
-usbip_common_mod-y := usbip_common.o usbip_event.o
+ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG
-obj-$(CONFIG_USB_IP_VHCI_HCD) += vhci-hcd.o
-vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
-
-obj-$(CONFIG_USB_IP_HOST) += usbip.o
-usbip-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o
+obj-$(CONFIG_USBIP_CORE) += usbip-core.o
+usbip-core-y := usbip_common.o usbip_event.o
-ccflags-$(CONFIG_USB_IP_DEBUG_ENABLE) := -DDEBUG
+obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o
+vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
+obj-$(CONFIG_USBIP_HOST) += usbip-host.o
+usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o
diff --git a/drivers/staging/usbip/README b/drivers/staging/usbip/README
index c11be573548..41a2cf2e77a 100644
--- a/drivers/staging/usbip/README
+++ b/drivers/staging/usbip/README
@@ -2,5 +2,6 @@ TODO:
- more discussion about the protocol
- testing
- review of the userspace interface
+ - document the protocol
Please send patches for this code to Greg Kroah-Hartman <greg@kroah.com>
diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index 30dbfb6d16f..266e2b0ce9a 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -17,13 +17,15 @@
* USA.
*/
-#include <linux/kernel.h>
+#ifndef __USBIP_STUB_H
+#define __USBIP_STUB_H
+
#include <linux/list.h>
-#include <linux/spinlock.h>
#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/net.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
#define STUB_BUSID_OTHER 0
#define STUB_BUSID_REMOV 1
@@ -32,7 +34,7 @@
struct stub_device {
struct usb_interface *interface;
- struct list_head list;
+ struct usb_device *udev;
struct usbip_device ud;
__u32 devid;
@@ -57,7 +59,6 @@ struct stub_device {
struct list_head unlink_tx;
struct list_head unlink_free;
-
wait_queue_head_t tx_waitq;
};
@@ -77,34 +78,36 @@ struct stub_unlink {
__u32 status;
};
-#define BUSID_SIZE 20
+/* same as SYSFS_BUS_ID_SIZE */
+#define BUSID_SIZE 32
+
struct bus_id_priv {
char name[BUSID_SIZE];
char status;
int interf_count;
struct stub_device *sdev;
+ struct usb_device *udev;
char shutdown_busid;
};
+/* stub_priv is allocated from stub_priv_cache */
extern struct kmem_cache *stub_priv_cache;
-
-/*-------------------------------------------------------------------------*/
-/* prototype declarations */
-
-/* stub_tx.c */
-void stub_complete(struct urb *);
-void stub_tx_loop(struct usbip_task *);
-
/* stub_dev.c */
-extern struct usb_driver stub_driver;
-
-/* stub_rx.c */
-void stub_rx_loop(struct usbip_task *);
-void stub_enqueue_ret_unlink(struct stub_device *, __u32, __u32);
+extern struct usb_device_driver stub_driver;
/* stub_main.c */
struct bus_id_priv *get_busid_priv(const char *busid);
int del_match_busid(char *busid);
-
void stub_device_cleanup_urbs(struct stub_device *sdev);
+
+/* stub_rx.c */
+int stub_rx_loop(void *data);
+
+/* stub_tx.c */
+void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
+ __u32 status);
+void stub_complete(struct urb *urb);
+int stub_tx_loop(void *data);
+
+#endif /* __USBIP_STUB_H */
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index b186b5fed2b..51d0c718873 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -17,22 +17,18 @@
* USA.
*/
-#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
#include "usbip_common.h"
#include "stub.h"
-
-
-static int stub_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void stub_disconnect(struct usb_interface *interface);
-
-
/*
* Define device IDs here if you want to explicitly limit exportable devices.
- * In the most cases, wild card matching will be ok because driver binding can
- * be changed dynamically by a userland program.
+ * In most cases, wildcard matching will be okay because driver binding can be
+ * changed dynamically by a userland program.
*/
static struct usb_device_id stub_table[] = {
#if 0
@@ -56,25 +52,12 @@ static struct usb_device_id stub_table[] = {
};
MODULE_DEVICE_TABLE(usb, stub_table);
-struct usb_driver stub_driver = {
- .name = "usbip",
- .probe = stub_probe,
- .disconnect = stub_disconnect,
- .id_table = stub_table,
-};
-
-
-/*-------------------------------------------------------------------------*/
-
-/* Define sysfs entries for a usbip-bound device */
-
-
/*
- * usbip_status shows status of usbip as long as this driver is bound to the
- * target device.
+ * usbip_status shows the status of usbip-host as long as this driver is bound
+ * to the target device.
*/
-static ssize_t show_status(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t usbip_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct stub_device *sdev = dev_get_drvdata(dev);
int status;
@@ -84,13 +67,13 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr,
return -ENODEV;
}
- spin_lock(&sdev->ud.lock);
+ spin_lock_irq(&sdev->ud.lock);
status = sdev->ud.status;
- spin_unlock(&sdev->ud.lock);
+ spin_unlock_irq(&sdev->ud.lock);
return snprintf(buf, PAGE_SIZE, "%d\n", status);
}
-static DEVICE_ATTR(usbip_status, S_IRUGO, show_status, NULL);
+static DEVICE_ATTR_RO(usbip_status);
/*
* usbip_sockfd gets a socket descriptor of an established TCP connection that
@@ -103,61 +86,63 @@ 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) {
dev_err(dev, "sdev is null\n");
return -ENODEV;
}
- sscanf(buf, "%d", &sockfd);
+ rv = sscanf(buf, "%d", &sockfd);
+ if (rv != 1)
+ return -EINVAL;
if (sockfd != -1) {
+ int err;
+
dev_info(dev, "stub up\n");
- spin_lock(&sdev->ud.lock);
+ spin_lock_irq(&sdev->ud.lock);
if (sdev->ud.status != SDEV_ST_AVAILABLE) {
dev_err(dev, "not ready\n");
- spin_unlock(&sdev->ud.lock);
- return -EINVAL;
+ goto err;
}
- socket = sockfd_to_socket(sockfd);
- if (!socket) {
- spin_unlock(&sdev->ud.lock);
- return -EINVAL;
- }
-
-#if 0
- setnodelay(socket);
- setkeepalive(socket);
- setreuse(socket);
-#endif
+ socket = sockfd_lookup(sockfd, &err);
+ if (!socket)
+ goto err;
sdev->ud.tcp_socket = socket;
- spin_unlock(&sdev->ud.lock);
+ spin_unlock_irq(&sdev->ud.lock);
- usbip_start_threads(&sdev->ud);
+ sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
+ "stub_rx");
+ sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
+ "stub_tx");
- spin_lock(&sdev->ud.lock);
+ spin_lock_irq(&sdev->ud.lock);
sdev->ud.status = SDEV_ST_USED;
- spin_unlock(&sdev->ud.lock);
+ spin_unlock_irq(&sdev->ud.lock);
} else {
dev_info(dev, "stub down\n");
- spin_lock(&sdev->ud.lock);
- if (sdev->ud.status != SDEV_ST_USED) {
- spin_unlock(&sdev->ud.lock);
- return -EINVAL;
- }
- spin_unlock(&sdev->ud.lock);
+ spin_lock_irq(&sdev->ud.lock);
+ if (sdev->ud.status != SDEV_ST_USED)
+ goto err;
+
+ spin_unlock_irq(&sdev->ud.lock);
usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
}
return count;
+
+err:
+ spin_unlock_irq(&sdev->ud.lock);
+ return -EINVAL;
}
static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
@@ -181,10 +166,8 @@ static int stub_add_files(struct device *dev)
err_debug:
device_remove_file(dev, &dev_attr_usbip_sockfd);
-
err_sockfd:
device_remove_file(dev, &dev_attr_usbip_status);
-
err_status:
return err;
}
@@ -196,12 +179,6 @@ static void stub_remove_files(struct device *dev)
device_remove_file(dev, &dev_attr_usbip_debug);
}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* Event handler functions called by an event handler thread */
-
static void stub_shutdown_connection(struct usbip_device *ud)
{
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
@@ -213,20 +190,29 @@ static void stub_shutdown_connection(struct usbip_device *ud)
* step 1?
*/
if (ud->tcp_socket) {
- usbip_udbg("shutdown tcp_socket %p\n", ud->tcp_socket);
+ dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n",
+ ud->tcp_socket);
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
/* 1. stop threads */
- usbip_stop_threads(ud);
+ if (ud->tcp_rx) {
+ kthread_stop_put(ud->tcp_rx);
+ ud->tcp_rx = NULL;
+ }
+ if (ud->tcp_tx) {
+ kthread_stop_put(ud->tcp_tx);
+ ud->tcp_tx = NULL;
+ }
- /* 2. close the socket */
/*
- * tcp_socket is freed after threads are killed.
- * So usbip_xmit do not touch NULL socket.
+ * 2. close the socket
+ *
+ * tcp_socket is freed after threads are killed so that usbip_xmit does
+ * not touch NULL socket.
*/
if (ud->tcp_socket) {
- sock_release(ud->tcp_socket);
+ sockfd_put(ud->tcp_socket);
ud->tcp_socket = NULL;
}
@@ -239,18 +225,15 @@ static void stub_shutdown_connection(struct usbip_device *ud)
struct stub_unlink *unlink, *tmp;
spin_lock_irqsave(&sdev->priv_lock, flags);
-
list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
list_del(&unlink->list);
kfree(unlink);
}
-
- list_for_each_entry_safe(unlink, tmp,
- &sdev->unlink_free, list) {
+ list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free,
+ list) {
list_del(&unlink->list);
kfree(unlink);
}
-
spin_unlock_irqrestore(&sdev->priv_lock, flags);
}
}
@@ -258,96 +241,79 @@ static void stub_shutdown_connection(struct usbip_device *ud)
static void stub_device_reset(struct usbip_device *ud)
{
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
- struct usb_device *udev = interface_to_usbdev(sdev->interface);
+ struct usb_device *udev = sdev->udev;
int ret;
- usbip_udbg("device reset");
+ dev_dbg(&udev->dev, "device reset");
+
ret = usb_lock_device_for_reset(udev, sdev->interface);
if (ret < 0) {
dev_err(&udev->dev, "lock for reset\n");
-
- spin_lock(&ud->lock);
+ spin_lock_irq(&ud->lock);
ud->status = SDEV_ST_ERROR;
- spin_unlock(&ud->lock);
-
+ spin_unlock_irq(&ud->lock);
return;
}
/* try to reset the device */
ret = usb_reset_device(udev);
-
usb_unlock_device(udev);
- spin_lock(&ud->lock);
+ spin_lock_irq(&ud->lock);
if (ret) {
dev_err(&udev->dev, "device reset\n");
ud->status = SDEV_ST_ERROR;
-
} else {
dev_info(&udev->dev, "device reset\n");
ud->status = SDEV_ST_AVAILABLE;
-
}
- spin_unlock(&ud->lock);
-
- return;
+ spin_unlock_irq(&ud->lock);
}
static void stub_device_unusable(struct usbip_device *ud)
{
- spin_lock(&ud->lock);
+ spin_lock_irq(&ud->lock);
ud->status = SDEV_ST_ERROR;
- spin_unlock(&ud->lock);
+ spin_unlock_irq(&ud->lock);
}
-
-/*-------------------------------------------------------------------------*/
-
/**
* stub_device_alloc - allocate a new stub_device struct
* @interface: usb_interface of a new device
*
* Allocates and initializes a new stub_device struct.
*/
-static struct stub_device *stub_device_alloc(struct usb_interface *interface)
+static struct stub_device *stub_device_alloc(struct usb_device *udev)
{
struct stub_device *sdev;
- int busnum = interface_to_busnum(interface);
- int devnum = interface_to_devnum(interface);
+ int busnum = udev->bus->busnum;
+ int devnum = udev->devnum;
- dev_dbg(&interface->dev, "allocating stub device");
+ dev_dbg(&udev->dev, "allocating stub device");
/* yes, it's a new device */
sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
- if (!sdev) {
- dev_err(&interface->dev, "no memory for stub_device\n");
+ if (!sdev)
return NULL;
- }
- sdev->interface = interface;
+ sdev->udev = usb_get_dev(udev);
/*
* devid is defined with devnum when this driver is first allocated.
* devnum may change later if a device is reset. However, devid never
* changes during a usbip connection.
*/
- sdev->devid = (busnum << 16) | devnum;
-
- usbip_task_init(&sdev->ud.tcp_rx, "stub_rx", stub_rx_loop);
- usbip_task_init(&sdev->ud.tcp_tx, "stub_tx", stub_tx_loop);
-
- sdev->ud.side = USBIP_STUB;
- sdev->ud.status = SDEV_ST_AVAILABLE;
- /* sdev->ud.lock = SPIN_LOCK_UNLOCKED; */
+ sdev->devid = (busnum << 16) | devnum;
+ sdev->ud.side = USBIP_STUB;
+ sdev->ud.status = SDEV_ST_AVAILABLE;
spin_lock_init(&sdev->ud.lock);
- sdev->ud.tcp_socket = NULL;
+ sdev->ud.tcp_socket = NULL;
INIT_LIST_HEAD(&sdev->priv_init);
INIT_LIST_HEAD(&sdev->priv_tx);
INIT_LIST_HEAD(&sdev->priv_free);
INIT_LIST_HEAD(&sdev->unlink_free);
INIT_LIST_HEAD(&sdev->unlink_tx);
- /* sdev->priv_lock = SPIN_LOCK_UNLOCKED; */
spin_lock_init(&sdev->priv_lock);
init_waitqueue_head(&sdev->tx_waitq);
@@ -358,52 +324,33 @@ static struct stub_device *stub_device_alloc(struct usb_interface *interface)
usbip_start_eh(&sdev->ud);
- usbip_udbg("register new interface\n");
+ dev_dbg(&udev->dev, "register new device\n");
+
return sdev;
}
-static int stub_device_free(struct stub_device *sdev)
+static void stub_device_free(struct stub_device *sdev)
{
- if (!sdev)
- return -EINVAL;
-
kfree(sdev);
- usbip_udbg("kfree udev ok\n");
-
- return 0;
}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * If a usb device has multiple active interfaces, this driver is bound to all
- * the active interfaces. However, usbip exports *a* usb device (i.e., not *an*
- * active interface). Currently, a userland program must ensure that it
- * looks at the usbip's sysfs entries of only the first active interface.
- *
- * TODO: use "struct usb_device_driver" to bind a usb device.
- * However, it seems it is not fully supported in mainline kernel yet
- * (2.6.19.2).
- */
-static int stub_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
+static int stub_probe(struct usb_device *udev)
{
- struct usb_device *udev = interface_to_usbdev(interface);
struct stub_device *sdev = NULL;
- const char *udev_busid = dev_name(interface->dev.parent);
+ const char *udev_busid = dev_name(&udev->dev);
int err = 0;
struct bus_id_priv *busid_priv;
+ int rc;
- dev_dbg(&interface->dev, "Enter\n");
+ dev_dbg(&udev->dev, "Enter\n");
/* check we should claim or not by busid_table */
busid_priv = get_busid_priv(udev_busid);
- if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) ||
- (busid_priv->status == STUB_BUSID_OTHER)) {
- dev_info(&interface->dev,
- "this device %s is not in match_busid table. skip!\n",
- udev_busid);
+ if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) ||
+ (busid_priv->status == STUB_BUSID_OTHER)) {
+ dev_info(&udev->dev,
+ "%s is not in match_busid table... skip!\n",
+ udev_busid);
/*
* Return value should be ENODEV or ENOXIO to continue trying
@@ -413,70 +360,54 @@ static int stub_probe(struct usb_interface *interface,
return -ENODEV;
}
- if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
- usbip_udbg("this device %s is a usb hub device. skip!\n",
- udev_busid);
+ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
+ dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
+ udev_busid);
return -ENODEV;
}
if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
- usbip_udbg("this device %s is attached on vhci_hcd. skip!\n",
- udev_busid);
- return -ENODEV;
- }
-
-
- if (busid_priv->status == STUB_BUSID_ALLOC) {
- sdev = busid_priv->sdev;
- if (!sdev)
- return -ENODEV;
-
- busid_priv->interf_count++;
- dev_info(&interface->dev,
- "USB/IP Stub: register a new interface "
- "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum,
- interface->cur_altsetting->desc.bInterfaceNumber);
-
- /* set private data to usb_interface */
- usb_set_intfdata(interface, sdev);
-
- err = stub_add_files(&interface->dev);
- if (err) {
- dev_err(&interface->dev, "create sysfs files for %s\n",
- udev_busid);
- usb_set_intfdata(interface, NULL);
- busid_priv->interf_count--;
-
- return err;
- }
+ dev_dbg(&udev->dev,
+ "%s is attached on vhci_hcd... skip!\n",
+ udev_busid);
- return 0;
+ return -ENODEV;
}
- /* ok. this is my device. */
- sdev = stub_device_alloc(interface);
+ /* ok, this is my device */
+ sdev = stub_device_alloc(udev);
if (!sdev)
return -ENOMEM;
- dev_info(&interface->dev, "USB/IP Stub: register a new device "
- "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum,
- interface->cur_altsetting->desc.bInterfaceNumber);
+ dev_info(&udev->dev,
+ "usbip-host: register new device (bus %u dev %u)\n",
+ udev->bus->busnum, udev->devnum);
- busid_priv->interf_count = 0;
busid_priv->shutdown_busid = 0;
- /* set private data to usb_interface */
- usb_set_intfdata(interface, sdev);
- busid_priv->interf_count++;
-
+ /* set private data to usb_device */
+ dev_set_drvdata(&udev->dev, sdev);
busid_priv->sdev = sdev;
+ busid_priv->udev = udev;
+
+ /*
+ * Claim this hub port.
+ * It doesn't matter what value we pass as owner
+ * (struct dev_state) as long as it is unique.
+ */
+ rc = usb_hub_claim_port(udev->parent, udev->portnum,
+ (struct usb_dev_state *) udev);
+ if (rc) {
+ dev_dbg(&udev->dev, "unable to claim port\n");
+ return rc;
+ }
- err = stub_add_files(&interface->dev);
+ err = stub_add_files(&udev->dev);
if (err) {
- dev_err(&interface->dev, "create sysfs files for %s\n",
- udev_busid);
- usb_set_intfdata(interface, NULL);
- busid_priv->interf_count = 0;
+ dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid);
+ dev_set_drvdata(&udev->dev, NULL);
+ usb_put_dev(udev);
+ kthread_stop_put(sdev->ud.eh);
busid_priv->sdev = NULL;
stub_device_free(sdev);
@@ -493,68 +424,63 @@ static void shutdown_busid(struct bus_id_priv *busid_priv)
busid_priv->shutdown_busid = 1;
usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED);
- /* 2. wait for the stop of the event handler */
+ /* wait for the stop of the event handler */
usbip_stop_eh(&busid_priv->sdev->ud);
}
-
}
-
/*
* called in usb_disconnect() or usb_deregister()
* but only if actconfig(active configuration) exists
*/
-static void stub_disconnect(struct usb_interface *interface)
+static void stub_disconnect(struct usb_device *udev)
{
struct stub_device *sdev;
- const char *udev_busid = dev_name(interface->dev.parent);
+ const char *udev_busid = dev_name(&udev->dev);
struct bus_id_priv *busid_priv;
+ int rc;
- busid_priv = get_busid_priv(udev_busid);
-
- usbip_udbg("Enter\n");
+ dev_dbg(&udev->dev, "Enter\n");
+ busid_priv = get_busid_priv(udev_busid);
if (!busid_priv) {
BUG();
return;
}
- sdev = usb_get_intfdata(interface);
+ sdev = dev_get_drvdata(&udev->dev);
/* get stub_device */
if (!sdev) {
- err(" could not get device from inteface data");
- /* BUG(); */
+ dev_err(&udev->dev, "could not get device");
return;
}
- usb_set_intfdata(interface, NULL);
+ dev_set_drvdata(&udev->dev, NULL);
/*
- * NOTE:
- * rx/tx threads are invoked for each usb_device.
+ * NOTE: rx/tx threads are invoked for each usb_device.
*/
- stub_remove_files(&interface->dev);
+ stub_remove_files(&udev->dev);
- /*If usb reset called from event handler*/
- if (busid_priv->sdev->ud.eh.thread == current) {
- busid_priv->interf_count--;
+ /* release port */
+ rc = usb_hub_release_port(udev->parent, udev->portnum,
+ (struct usb_dev_state *) udev);
+ if (rc) {
+ dev_dbg(&udev->dev, "unable to release port\n");
return;
}
- if (busid_priv->interf_count > 1) {
- busid_priv->interf_count--;
- shutdown_busid(busid_priv);
+ /* If usb reset is called from event handler */
+ if (busid_priv->sdev->ud.eh == current)
return;
- }
- busid_priv->interf_count = 0;
-
-
- /* 1. shutdown the current connection */
+ /* shutdown the current connection */
shutdown_busid(busid_priv);
- /* 3. free sdev */
+ usb_put_dev(sdev->udev);
+
+ /* free sdev */
busid_priv->sdev = NULL;
stub_device_free(sdev);
@@ -564,5 +490,36 @@ static void stub_disconnect(struct usb_interface *interface)
busid_priv->status = STUB_BUSID_OTHER;
del_match_busid((char *)udev_busid);
}
- usbip_udbg("bye\n");
}
+
+#ifdef CONFIG_PM
+
+/* These functions need usb_port_suspend and usb_port_resume,
+ * which reside in drivers/usb/core/usb.h. Skip for now. */
+
+static int stub_suspend(struct usb_device *udev, pm_message_t message)
+{
+ dev_dbg(&udev->dev, "stub_suspend\n");
+
+ return 0;
+}
+
+static int stub_resume(struct usb_device *udev, pm_message_t message)
+{
+ dev_dbg(&udev->dev, "stub_resume\n");
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+struct usb_device_driver stub_driver = {
+ .name = "usbip-host",
+ .probe = stub_probe,
+ .disconnect = stub_disconnect,
+#ifdef CONFIG_PM
+ .suspend = stub_suspend,
+ .resume = stub_resume,
+#endif
+ .supports_autosuspend = 0,
+};
diff --git a/drivers/staging/usbip/stub_main.c b/drivers/staging/usbip/stub_main.c
index f3a40968aae..9c5832abbdf 100644
--- a/drivers/staging/usbip/stub_main.c
+++ b/drivers/staging/usbip/stub_main.c
@@ -17,24 +17,17 @@
* USA.
*/
-#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/device.h>
#include "usbip_common.h"
#include "stub.h"
-/* Version Information */
-#define DRIVER_VERSION "1.0"
#define DRIVER_AUTHOR "Takahiro Hirofuchi"
-#define DRIVER_DESC "Stub Driver for USB/IP"
+#define DRIVER_DESC "USB/IP Host Driver"
-/* stub_priv is allocated from stub_priv_cache */
struct kmem_cache *stub_priv_cache;
-
-/*-------------------------------------------------------------------------*/
-
-/* Define sysfs entries for the usbip driver */
-
-
/*
* busid_tables defines matching busids that usbip can grab. A user can change
* dynamically what device is locally used and what device is exported to a
@@ -44,71 +37,60 @@ struct kmem_cache *stub_priv_cache;
static struct bus_id_priv busid_table[MAX_BUSID];
static spinlock_t busid_table_lock;
-
-int match_busid(const char *busid)
+static void init_busid_table(void)
{
- int i;
-
- spin_lock(&busid_table_lock);
-
- for (i = 0; i < MAX_BUSID; i++)
- if (busid_table[i].name[0])
- if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
- /* already registerd */
- spin_unlock(&busid_table_lock);
- return 0;
- }
-
- spin_unlock(&busid_table_lock);
+ /*
+ * This also sets the bus_table[i].status to
+ * STUB_BUSID_OTHER, which is 0.
+ */
+ memset(busid_table, 0, sizeof(busid_table));
- return 1;
+ spin_lock_init(&busid_table_lock);
}
-struct bus_id_priv *get_busid_priv(const char *busid)
+/*
+ * Find the index of the busid by name.
+ * Must be called with busid_table_lock held.
+ */
+static int get_busid_idx(const char *busid)
{
int i;
-
- spin_lock(&busid_table_lock);
+ int idx = -1;
for (i = 0; i < MAX_BUSID; i++)
if (busid_table[i].name[0])
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
- /* already registerd */
- spin_unlock(&busid_table_lock);
- return &(busid_table[i]);
+ idx = i;
+ break;
}
-
- spin_unlock(&busid_table_lock);
-
- return NULL;
+ return idx;
}
-static ssize_t show_match_busid(struct device_driver *drv, char *buf)
+struct bus_id_priv *get_busid_priv(const char *busid)
{
- int i;
- char *out = buf;
+ int idx;
+ struct bus_id_priv *bid = NULL;
spin_lock(&busid_table_lock);
-
- for (i = 0; i < MAX_BUSID; i++)
- if (busid_table[i].name[0])
- out += sprintf(out, "%s ", busid_table[i].name);
-
+ idx = get_busid_idx(busid);
+ if (idx >= 0)
+ bid = &(busid_table[idx]);
spin_unlock(&busid_table_lock);
- out += sprintf(out, "\n");
-
- return out - buf;
+ return bid;
}
static int add_match_busid(char *busid)
{
int i;
-
- if (!match_busid(busid))
- return 0;
+ int ret = -1;
spin_lock(&busid_table_lock);
+ /* already registered? */
+ if (get_busid_idx(busid) >= 0) {
+ ret = 0;
+ goto out;
+ }
for (i = 0; i < MAX_BUSID; i++)
if (!busid_table[i].name[0]) {
@@ -116,55 +98,59 @@ static int add_match_busid(char *busid)
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
(busid_table[i].status != STUB_BUSID_REMOV))
busid_table[i].status = STUB_BUSID_ADDED;
- spin_unlock(&busid_table_lock);
- return 0;
+ ret = 0;
+ break;
}
+out:
spin_unlock(&busid_table_lock);
- return -1;
+ return ret;
}
int del_match_busid(char *busid)
{
- int i;
+ int idx;
+ int ret = -1;
spin_lock(&busid_table_lock);
+ idx = get_busid_idx(busid);
+ if (idx < 0)
+ goto out;
- for (i = 0; i < MAX_BUSID; i++)
- if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
- /* found */
- if (busid_table[i].status == STUB_BUSID_OTHER)
- memset(busid_table[i].name, 0, BUSID_SIZE);
- if ((busid_table[i].status != STUB_BUSID_OTHER) &&
- (busid_table[i].status != STUB_BUSID_ADDED)) {
- busid_table[i].status = STUB_BUSID_REMOV;
- }
- spin_unlock(&busid_table_lock);
- return 0;
- }
+ /* found */
+ ret = 0;
+ if (busid_table[idx].status == STUB_BUSID_OTHER)
+ memset(busid_table[idx].name, 0, BUSID_SIZE);
+
+ if ((busid_table[idx].status != STUB_BUSID_OTHER) &&
+ (busid_table[idx].status != STUB_BUSID_ADDED))
+ busid_table[idx].status = STUB_BUSID_REMOV;
+
+out:
spin_unlock(&busid_table_lock);
- return -1;
+ return ret;
}
-static void init_busid_table(void)
+
+static ssize_t show_match_busid(struct device_driver *drv, char *buf)
{
int i;
+ char *out = buf;
+ spin_lock(&busid_table_lock);
+ for (i = 0; i < MAX_BUSID; i++)
+ if (busid_table[i].name[0])
+ out += sprintf(out, "%s ", busid_table[i].name);
+ spin_unlock(&busid_table_lock);
+ out += sprintf(out, "\n");
- for (i = 0; i < MAX_BUSID; i++) {
- memset(busid_table[i].name, 0, BUSID_SIZE);
- busid_table[i].status = STUB_BUSID_OTHER;
- busid_table[i].interf_count = 0;
- busid_table[i].sdev = NULL;
- busid_table[i].shutdown_busid = 0;
- }
- spin_lock_init(&busid_table_lock);
+ return out - buf;
}
static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
- size_t count)
+ size_t count)
{
int len;
char busid[BUSID_SIZE];
@@ -181,33 +167,54 @@ static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
strncpy(busid, buf + 4, BUSID_SIZE);
-
if (!strncmp(buf, "add ", 4)) {
if (add_match_busid(busid) < 0)
return -ENOMEM;
- else {
- usbip_udbg("add busid %s\n", busid);
- return count;
- }
- } else if (!strncmp(buf, "del ", 4)) {
+
+ pr_debug("add busid %s\n", busid);
+ return count;
+ }
+
+ if (!strncmp(buf, "del ", 4)) {
if (del_match_busid(busid) < 0)
return -ENODEV;
- else {
- usbip_udbg("del busid %s\n", busid);
- return count;
- }
- } else
- return -EINVAL;
+
+ pr_debug("del busid %s\n", busid);
+ return count;
+ }
+
+ return -EINVAL;
}
+static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid,
+ store_match_busid);
-static DRIVER_ATTR(match_busid, S_IRUSR|S_IWUSR, show_match_busid,
- store_match_busid);
+static ssize_t rebind_store(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ int ret;
+ int len;
+ struct bus_id_priv *bid;
+
+ /* buf length should be less that BUSID_SIZE */
+ len = strnlen(buf, BUSID_SIZE);
+
+ if (!(len < BUSID_SIZE))
+ return -EINVAL;
+ bid = get_busid_priv(buf);
+ if (!bid)
+ return -ENODEV;
+ ret = device_attach(&bid->udev->dev);
+ if (ret < 0) {
+ dev_err(&bid->udev->dev, "rebind failed\n");
+ return ret;
+ }
-/*-------------------------------------------------------------------------*/
+ return count;
+}
-/* Cleanup functions used to free private data */
+static DRIVER_ATTR_WO(rebind);
static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
{
@@ -229,113 +236,104 @@ static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
spin_lock_irqsave(&sdev->priv_lock, flags);
priv = stub_priv_pop_from_listhead(&sdev->priv_init);
- if (priv) {
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
- return priv;
- }
+ if (priv)
+ goto done;
priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
- if (priv) {
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
- return priv;
- }
+ if (priv)
+ goto done;
priv = stub_priv_pop_from_listhead(&sdev->priv_free);
- if (priv) {
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
- return priv;
- }
+done:
spin_unlock_irqrestore(&sdev->priv_lock, flags);
- return NULL;
+
+ return priv;
}
void stub_device_cleanup_urbs(struct stub_device *sdev)
{
struct stub_priv *priv;
+ struct urb *urb;
- usbip_udbg("free sdev %p\n", sdev);
+ dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev);
while ((priv = stub_priv_pop(sdev))) {
- struct urb *urb = priv->urb;
-
- usbip_udbg(" free urb %p\n", urb);
+ urb = priv->urb;
+ dev_dbg(&sdev->udev->dev, "free urb %p\n", urb);
usb_kill_urb(urb);
kmem_cache_free(stub_priv_cache, priv);
- if (urb->transfer_buffer != NULL)
- kfree(urb->transfer_buffer);
-
- if (urb->setup_packet != NULL)
- kfree(urb->setup_packet);
-
+ kfree(urb->transfer_buffer);
+ kfree(urb->setup_packet);
usb_free_urb(urb);
}
}
-
-/*-------------------------------------------------------------------------*/
-
-static int __init usb_stub_init(void)
+static int __init usbip_host_init(void)
{
int ret;
- stub_priv_cache = kmem_cache_create("stub_priv",
- sizeof(struct stub_priv), 0,
- SLAB_HWCACHE_ALIGN, NULL);
+ init_busid_table();
+ stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN);
if (!stub_priv_cache) {
- printk(KERN_ERR KBUILD_MODNAME
- ": create stub_priv_cache error\n");
+ pr_err("kmem_cache_create failed\n");
return -ENOMEM;
}
- ret = usb_register(&stub_driver);
+ ret = usb_register_device_driver(&stub_driver, THIS_MODULE);
if (ret) {
- printk(KERN_ERR KBUILD_MODNAME ": usb_register failed %d\n",
- ret);
- goto error_usb_register;
+ pr_err("usb_register failed %d\n", ret);
+ goto err_usb_register;
}
- printk(KERN_INFO KBUILD_MODNAME ":"
- DRIVER_DESC ":" DRIVER_VERSION "\n");
-
- init_busid_table();
-
ret = driver_create_file(&stub_driver.drvwrap.driver,
&driver_attr_match_busid);
+ if (ret) {
+ pr_err("driver_create_file failed\n");
+ goto err_create_file;
+ }
+ ret = driver_create_file(&stub_driver.drvwrap.driver,
+ &driver_attr_rebind);
if (ret) {
- printk(KERN_ERR KBUILD_MODNAME ": create driver sysfs\n");
- goto error_create_file;
+ pr_err("driver_create_file failed\n");
+ goto err_create_file;
}
+ pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
return ret;
-error_create_file:
- usb_deregister(&stub_driver);
-error_usb_register:
+
+err_create_file:
+ usb_deregister_device_driver(&stub_driver);
+err_usb_register:
kmem_cache_destroy(stub_priv_cache);
return ret;
}
-static void __exit usb_stub_exit(void)
+static void __exit usbip_host_exit(void)
{
driver_remove_file(&stub_driver.drvwrap.driver,
&driver_attr_match_busid);
+ driver_remove_file(&stub_driver.drvwrap.driver,
+ &driver_attr_rebind);
+
/*
* deregister() calls stub_disconnect() for all devices. Device
* specific data is cleared in stub_disconnect().
*/
- usb_deregister(&stub_driver);
+ usb_deregister_device_driver(&stub_driver);
kmem_cache_destroy(stub_priv_cache);
}
-module_init(usb_stub_init);
-module_exit(usb_stub_exit);
+module_init(usbip_host_init);
+module_exit(usbip_host_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION(USBIP_VERSION);
diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c
index 3de6fd2539d..e0b6d6b4272 100644
--- a/drivers/staging/usbip/stub_rx.c
+++ b/drivers/staging/usbip/stub_rx.c
@@ -17,12 +17,13 @@
* USA.
*/
-#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <linux/kthread.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
#include "usbip_common.h"
#include "stub.h"
-#include <linux/usb/hcd.h>
-
static int is_clear_halt_cmd(struct urb *urb)
{
@@ -42,7 +43,7 @@ static int is_set_interface_cmd(struct urb *urb)
req = (struct usb_ctrlrequest *) urb->setup_packet;
return (req->bRequest == USB_REQ_SET_INTERFACE) &&
- (req->bRequestType == USB_RECIP_INTERFACE);
+ (req->bRequestType == USB_RECIP_INTERFACE);
}
static int is_set_configuration_cmd(struct urb *urb)
@@ -52,7 +53,7 @@ static int is_set_configuration_cmd(struct urb *urb)
req = (struct usb_ctrlrequest *) urb->setup_packet;
return (req->bRequest == USB_REQ_SET_CONFIGURATION) &&
- (req->bRequestType == USB_RECIP_DEVICE);
+ (req->bRequestType == USB_RECIP_DEVICE);
}
static int is_reset_device_cmd(struct urb *urb)
@@ -66,8 +67,8 @@ static int is_reset_device_cmd(struct urb *urb)
index = le16_to_cpu(req->wIndex);
if ((req->bRequest == USB_REQ_SET_FEATURE) &&
- (req->bRequestType == USB_RT_PORT) &&
- (value == USB_PORT_FEAT_RESET)) {
+ (req->bRequestType == USB_RT_PORT) &&
+ (value == USB_PORT_FEAT_RESET)) {
usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index);
return 1;
} else
@@ -101,11 +102,13 @@ static int tweak_clear_halt_cmd(struct urb *urb)
ret = usb_clear_halt(urb->dev, target_pipe);
if (ret < 0)
- usbip_uinfo("clear_halt error: devnum %d endp %d, %d\n",
- urb->dev->devnum, target_endp, ret);
+ dev_err(&urb->dev->dev,
+ "usb_clear_halt error: devnum %d endp %d ret %d\n",
+ urb->dev->devnum, target_endp, ret);
else
- usbip_uinfo("clear_halt done: devnum %d endp %d\n",
- urb->dev->devnum, target_endp);
+ dev_info(&urb->dev->dev,
+ "usb_clear_halt done: devnum %d endp %d\n",
+ urb->dev->devnum, target_endp);
return ret;
}
@@ -121,82 +124,60 @@ static int tweak_set_interface_cmd(struct urb *urb)
alternate = le16_to_cpu(req->wValue);
interface = le16_to_cpu(req->wIndex);
- usbip_dbg_stub_rx("set_interface: inf %u alt %u\n", interface,
- alternate);
+ usbip_dbg_stub_rx("set_interface: inf %u alt %u\n",
+ interface, alternate);
ret = usb_set_interface(urb->dev, interface, alternate);
if (ret < 0)
- usbip_uinfo("set_interface error: inf %u alt %u, %d\n",
- interface, alternate, ret);
+ dev_err(&urb->dev->dev,
+ "usb_set_interface error: inf %u alt %u ret %d\n",
+ interface, alternate, ret);
else
- usbip_uinfo("set_interface done: inf %u alt %u\n",
- interface,
- alternate);
+ dev_info(&urb->dev->dev,
+ "usb_set_interface done: inf %u alt %u\n",
+ interface, alternate);
return ret;
}
static int tweak_set_configuration_cmd(struct urb *urb)
{
+ struct stub_priv *priv = (struct stub_priv *) urb->context;
+ struct stub_device *sdev = priv->sdev;
struct usb_ctrlrequest *req;
__u16 config;
+ int err;
req = (struct usb_ctrlrequest *) urb->setup_packet;
config = le16_to_cpu(req->wValue);
- /*
- * I have never seen a multi-config device. Very rare.
- * For most devices, this will be called to choose a default
- * configuration only once in an initialization phase.
- *
- * set_configuration may change a device configuration and its device
- * drivers will be unbound and assigned for a new device configuration.
- * This means this usbip driver will be also unbound when called, then
- * eventually reassigned to the device as far as driver matching
- * condition is kept.
- *
- * Unfortunatelly, an existing usbip connection will be dropped
- * due to this driver unbinding. So, skip here.
- * A user may need to set a special configuration value before
- * exporting the device.
- */
- usbip_uinfo("set_configuration (%d) to %s\n", config,
- dev_name(&urb->dev->dev));
- usbip_uinfo("but, skip!\n");
-
+ err = usb_set_configuration(sdev->udev, config);
+ if (err && err != -ENODEV)
+ dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n",
+ config, err);
return 0;
- /* return usb_driver_set_configuration(urb->dev, config); */
}
static int tweak_reset_device_cmd(struct urb *urb)
{
- struct usb_ctrlrequest *req;
- __u16 value;
- __u16 index;
- int ret;
+ struct stub_priv *priv = (struct stub_priv *) urb->context;
+ struct stub_device *sdev = priv->sdev;
- req = (struct usb_ctrlrequest *) urb->setup_packet;
- value = le16_to_cpu(req->wValue);
- index = le16_to_cpu(req->wIndex);
+ dev_info(&urb->dev->dev, "usb_queue_reset_device\n");
- usbip_uinfo("reset_device (port %d) to %s\n", index,
- dev_name(&urb->dev->dev));
+ /*
+ * With the implementation of pre_reset and post_reset the driver no
+ * longer unbinds. This allows the use of synchronous reset.
+ */
- /* all interfaces should be owned by usbip driver, so just reset it. */
- ret = usb_lock_device_for_reset(urb->dev, NULL);
- if (ret < 0) {
- dev_err(&urb->dev->dev, "lock for reset\n");
- return ret;
+ if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) {
+ dev_err(&urb->dev->dev, "could not obtain lock to reset device\n");
+ return 0;
}
+ usb_reset_device(sdev->udev);
+ usb_unlock_device(sdev->udev);
- /* try to reset the device */
- ret = usb_reset_device(urb->dev);
- if (ret < 0)
- dev_err(&urb->dev->dev, "device reset\n");
-
- usb_unlock_device(urb->dev);
-
- return ret;
+ return 0;
}
/*
@@ -237,68 +218,67 @@ static void tweak_special_requests(struct urb *urb)
* See also comments about unlinking strategy in vhci_hcd.c.
*/
static int stub_recv_cmd_unlink(struct stub_device *sdev,
- struct usbip_header *pdu)
+ struct usbip_header *pdu)
{
+ int ret;
unsigned long flags;
-
struct stub_priv *priv;
-
spin_lock_irqsave(&sdev->priv_lock, flags);
list_for_each_entry(priv, &sdev->priv_init, list) {
- if (priv->seqnum == pdu->u.cmd_unlink.seqnum) {
- int ret;
-
- dev_info(&priv->urb->dev->dev, "unlink urb %p\n",
- priv->urb);
-
- /*
- * This matched urb is not completed yet (i.e., be in
- * flight in usb hcd hardware/driver). Now we are
- * cancelling it. The unlinking flag means that we are
- * now not going to return the normal result pdu of a
- * submission request, but going to return a result pdu
- * of the unlink request.
- */
- priv->unlinking = 1;
-
- /*
- * In the case that unlinking flag is on, prev->seqnum
- * is changed from the seqnum of the cancelling urb to
- * the seqnum of the unlink request. This will be used
- * to make the result pdu of the unlink request.
- */
- priv->seqnum = pdu->base.seqnum;
-
- spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
- /*
- * usb_unlink_urb() is now out of spinlocking to avoid
- * spinlock recursion since stub_complete() is
- * sometimes called in this context but not in the
- * interrupt context. If stub_complete() is executed
- * before we call usb_unlink_urb(), usb_unlink_urb()
- * will return an error value. In this case, stub_tx
- * will return the result pdu of this unlink request
- * though submission is completed and actual unlinking
- * is not executed. OK?
- */
- /* In the above case, urb->status is not -ECONNRESET,
- * so a driver in a client host will know the failure
- * of the unlink request ?
- */
- ret = usb_unlink_urb(priv->urb);
- if (ret != -EINPROGRESS)
- dev_err(&priv->urb->dev->dev,
- "failed to unlink a urb %p, ret %d\n",
- priv->urb, ret);
- return 0;
- }
+ if (priv->seqnum != pdu->u.cmd_unlink.seqnum)
+ continue;
+
+ dev_info(&priv->urb->dev->dev, "unlink urb %p\n",
+ priv->urb);
+
+ /*
+ * This matched urb is not completed yet (i.e., be in
+ * flight in usb hcd hardware/driver). Now we are
+ * cancelling it. The unlinking flag means that we are
+ * now not going to return the normal result pdu of a
+ * submission request, but going to return a result pdu
+ * of the unlink request.
+ */
+ priv->unlinking = 1;
+
+ /*
+ * In the case that unlinking flag is on, prev->seqnum
+ * is changed from the seqnum of the cancelling urb to
+ * the seqnum of the unlink request. This will be used
+ * to make the result pdu of the unlink request.
+ */
+ priv->seqnum = pdu->base.seqnum;
+
+ spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+ /*
+ * usb_unlink_urb() is now out of spinlocking to avoid
+ * spinlock recursion since stub_complete() is
+ * sometimes called in this context but not in the
+ * interrupt context. If stub_complete() is executed
+ * before we call usb_unlink_urb(), usb_unlink_urb()
+ * will return an error value. In this case, stub_tx
+ * will return the result pdu of this unlink request
+ * though submission is completed and actual unlinking
+ * is not executed. OK?
+ */
+ /* In the above case, urb->status is not -ECONNRESET,
+ * so a driver in a client host will know the failure
+ * of the unlink request ?
+ */
+ ret = usb_unlink_urb(priv->urb);
+ if (ret != -EINPROGRESS)
+ dev_err(&priv->urb->dev->dev,
+ "failed to unlink a urb %p, ret %d\n",
+ priv->urb, ret);
+
+ return 0;
}
usbip_dbg_stub_rx("seqnum %d is not pending\n",
- pdu->u.cmd_unlink.seqnum);
+ pdu->u.cmd_unlink.seqnum);
/*
* The urb of the unlink target is not found in priv_init queue. It was
@@ -310,25 +290,24 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev,
spin_unlock_irqrestore(&sdev->priv_lock, flags);
-
return 0;
}
static int valid_request(struct stub_device *sdev, struct usbip_header *pdu)
{
struct usbip_device *ud = &sdev->ud;
+ int valid = 0;
if (pdu->base.devid == sdev->devid) {
- spin_lock(&ud->lock);
+ spin_lock_irq(&ud->lock);
if (ud->status == SDEV_ST_USED) {
/* A request is valid. */
- spin_unlock(&ud->lock);
- return 1;
+ valid = 1;
}
- spin_unlock(&ud->lock);
+ spin_unlock_irq(&ud->lock);
}
- return 0;
+ return valid;
}
static struct stub_priv *stub_priv_alloc(struct stub_device *sdev,
@@ -364,7 +343,7 @@ static struct stub_priv *stub_priv_alloc(struct stub_device *sdev,
static int get_pipe(struct stub_device *sdev, int epnum, int dir)
{
- struct usb_device *udev = interface_to_usbdev(sdev->interface);
+ struct usb_device *udev = sdev->udev;
struct usb_host_endpoint *ep;
struct usb_endpoint_descriptor *epd = NULL;
@@ -379,18 +358,6 @@ static int get_pipe(struct stub_device *sdev, int epnum, int dir)
}
epd = &ep->desc;
-
-
-#if 0
- /* epnum 0 is always control */
- if (epnum == 0) {
- if (dir == USBIP_DIR_OUT)
- return usb_sndctrlpipe(udev, 0);
- else
- return usb_rcvctrlpipe(udev, 0);
- }
-#endif
-
if (usb_endpoint_xfer_control(epd)) {
if (dir == USBIP_DIR_OUT)
return usb_sndctrlpipe(udev, epnum);
@@ -439,19 +406,19 @@ static void masking_bogus_flags(struct urb *urb)
return;
ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
- [usb_pipeendpoint(urb->pipe)];
+ [usb_pipeendpoint(urb->pipe)];
if (!ep)
return;
xfertype = usb_endpoint_type(&ep->desc);
if (xfertype == USB_ENDPOINT_XFER_CONTROL) {
struct usb_ctrlrequest *setup =
- (struct usb_ctrlrequest *) urb->setup_packet;
+ (struct usb_ctrlrequest *) urb->setup_packet;
if (!setup)
return;
is_out = !(setup->bRequestType & USB_DIR_IN) ||
- !setup->wLength;
+ !setup->wLength;
} else {
is_out = usb_endpoint_dir_out(&ep->desc);
}
@@ -484,10 +451,9 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
int ret;
struct stub_priv *priv;
struct usbip_device *ud = &sdev->ud;
- struct usb_device *udev = interface_to_usbdev(sdev->interface);
+ struct usb_device *udev = sdev->udev;
int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction);
-
priv = stub_priv_alloc(sdev, pdu);
if (!priv)
return;
@@ -495,7 +461,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
/* setup a urb */
if (usb_pipeisoc(pipe))
priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
- GFP_KERNEL);
+ GFP_KERNEL);
else
priv->urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -505,19 +471,18 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
return;
}
- /* set priv->urb->transfer_buffer */
+ /* allocate urb transfer buffer, if needed */
if (pdu->u.cmd_submit.transfer_buffer_length > 0) {
priv->urb->transfer_buffer =
kzalloc(pdu->u.cmd_submit.transfer_buffer_length,
- GFP_KERNEL);
+ GFP_KERNEL);
if (!priv->urb->transfer_buffer) {
- dev_err(&sdev->interface->dev, "malloc x_buff\n");
usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
return;
}
}
- /* set priv->urb->setup_packet */
+ /* copy urb setup packet */
priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
GFP_KERNEL);
if (!priv->urb->setup_packet) {
@@ -550,7 +515,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
if (ret == 0)
usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
- pdu->base.seqnum);
+ pdu->base.seqnum);
else {
dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret);
usbip_dump_header(pdu);
@@ -573,14 +538,14 @@ static void stub_rx_pdu(struct usbip_device *ud)
int ret;
struct usbip_header pdu;
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
- struct device *dev = &sdev->interface->dev;
+ struct device *dev = &sdev->udev->dev;
usbip_dbg_stub_rx("Enter\n");
memset(&pdu, 0, sizeof(pdu));
- /* 1. receive a pdu header */
- ret = usbip_xmit(0, ud->tcp_socket, (char *) &pdu, sizeof(pdu), 0);
+ /* receive a pdu header */
+ ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
if (ret != sizeof(pdu)) {
dev_err(dev, "recv a header, %d\n", ret);
usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
@@ -611,24 +576,20 @@ static void stub_rx_pdu(struct usbip_device *ud)
/* NOTREACHED */
dev_err(dev, "unknown pdu\n");
usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
- return;
+ break;
}
-
}
-void stub_rx_loop(struct usbip_task *ut)
+int stub_rx_loop(void *data)
{
- struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_rx);
-
- while (1) {
- if (signal_pending(current)) {
- usbip_dbg_stub_rx("signal caught!\n");
- break;
- }
+ struct usbip_device *ud = data;
+ while (!kthread_should_stop()) {
if (usbip_event_happened(ud))
break;
stub_rx_pdu(ud);
}
+
+ return 0;
}
diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c
index d7136e2c86f..dbcabc9dbe0 100644
--- a/drivers/staging/usbip/stub_tx.c
+++ b/drivers/staging/usbip/stub_tx.c
@@ -17,12 +17,12 @@
* USA.
*/
-#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/socket.h>
#include "usbip_common.h"
#include "stub.h"
-
static void stub_free_priv_and_urb(struct stub_priv *priv)
{
struct urb *urb = priv->urb;
@@ -42,7 +42,6 @@ void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC);
if (!unlink) {
- dev_err(&sdev->interface->dev, "alloc stub_unlink\n");
usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC);
return;
}
@@ -70,58 +69,54 @@ void stub_complete(struct urb *urb)
usbip_dbg_stub_tx("complete! status %d\n", urb->status);
-
switch (urb->status) {
case 0:
/* OK */
break;
case -ENOENT:
- usbip_uinfo("stopped by a call of usb_kill_urb() because of"
- "cleaning up a virtual connection\n");
+ dev_info(&urb->dev->dev,
+ "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
return;
case -ECONNRESET:
- usbip_uinfo("unlinked by a call of usb_unlink_urb()\n");
+ dev_info(&urb->dev->dev,
+ "unlinked by a call to usb_unlink_urb()\n");
break;
case -EPIPE:
- usbip_uinfo("endpoint %d is stalled\n",
- usb_pipeendpoint(urb->pipe));
+ dev_info(&urb->dev->dev, "endpoint %d is stalled\n",
+ usb_pipeendpoint(urb->pipe));
break;
case -ESHUTDOWN:
- usbip_uinfo("device removed?\n");
+ dev_info(&urb->dev->dev, "device removed?\n");
break;
default:
- usbip_uinfo("urb completion with non-zero status %d\n",
- urb->status);
+ dev_info(&urb->dev->dev,
+ "urb completion with non-zero status %d\n",
+ urb->status);
+ break;
}
/* link a urb to the queue of tx. */
spin_lock_irqsave(&sdev->priv_lock, flags);
-
if (priv->unlinking) {
stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status);
stub_free_priv_and_urb(priv);
- } else
+ } else {
list_move_tail(&priv->list, &sdev->priv_tx);
-
-
+ }
spin_unlock_irqrestore(&sdev->priv_lock, flags);
/* wake up tx_thread */
wake_up(&sdev->tx_waitq);
}
-
-/*-------------------------------------------------------------------------*/
-/* fill PDU */
-
static inline void setup_base_pdu(struct usbip_header_basic *base,
- __u32 command, __u32 seqnum)
+ __u32 command, __u32 seqnum)
{
- base->command = command;
- base->seqnum = seqnum;
- base->devid = 0;
- base->ep = 0;
- base->direction = 0;
+ base->command = command;
+ base->seqnum = seqnum;
+ base->devid = 0;
+ base->ep = 0;
+ base->direction = 0;
}
static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb)
@@ -129,22 +124,16 @@ static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb)
struct stub_priv *priv = (struct stub_priv *) urb->context;
setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum);
-
usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1);
}
static void setup_ret_unlink_pdu(struct usbip_header *rpdu,
- struct stub_unlink *unlink)
+ struct stub_unlink *unlink)
{
setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum);
-
rpdu->u.ret_unlink.status = unlink->status;
}
-
-/*-------------------------------------------------------------------------*/
-/* send RET_SUBMIT */
-
static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev)
{
unsigned long flags;
@@ -169,7 +158,6 @@ static int stub_send_ret_submit(struct stub_device *sdev)
struct stub_priv *priv, *tmp;
struct msghdr msg;
- struct kvec iov[3];
size_t txsize;
size_t total_size = 0;
@@ -178,29 +166,78 @@ static int stub_send_ret_submit(struct stub_device *sdev)
int ret;
struct urb *urb = priv->urb;
struct usbip_header pdu_header;
- void *iso_buffer = NULL;
+ struct usbip_iso_packet_descriptor *iso_buffer = NULL;
+ struct kvec *iov = NULL;
+ int iovnum = 0;
txsize = 0;
memset(&pdu_header, 0, sizeof(pdu_header));
memset(&msg, 0, sizeof(msg));
- memset(&iov, 0, sizeof(iov));
- usbip_dbg_stub_tx("setup txdata urb %p\n", urb);
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+ iovnum = 2 + urb->number_of_packets;
+ else
+ iovnum = 2;
+
+ iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL);
+
+ if (!iov) {
+ usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC);
+ return -1;
+ }
+ iovnum = 0;
/* 1. setup usbip_header */
setup_ret_submit_pdu(&pdu_header, urb);
+ usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
+ pdu_header.base.seqnum, urb);
usbip_header_correct_endian(&pdu_header, 1);
- iov[0].iov_base = &pdu_header;
- iov[0].iov_len = sizeof(pdu_header);
+ iov[iovnum].iov_base = &pdu_header;
+ iov[iovnum].iov_len = sizeof(pdu_header);
+ iovnum++;
txsize += sizeof(pdu_header);
/* 2. setup transfer buffer */
- if (usb_pipein(urb->pipe) && urb->actual_length > 0) {
- iov[1].iov_base = urb->transfer_buffer;
- iov[1].iov_len = urb->actual_length;
+ if (usb_pipein(urb->pipe) &&
+ usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS &&
+ urb->actual_length > 0) {
+ iov[iovnum].iov_base = urb->transfer_buffer;
+ iov[iovnum].iov_len = urb->actual_length;
+ iovnum++;
txsize += urb->actual_length;
+ } else if (usb_pipein(urb->pipe) &&
+ usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ /*
+ * For isochronous packets: actual length is the sum of
+ * the actual length of the individual, packets, but as
+ * the packet offsets are not changed there will be
+ * padding between the packets. To optimally use the
+ * bandwidth the padding is not transmitted.
+ */
+
+ int i;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ iov[iovnum].iov_base = urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+ iov[iovnum].iov_len =
+ urb->iso_frame_desc[i].actual_length;
+ iovnum++;
+ txsize += urb->iso_frame_desc[i].actual_length;
+ }
+
+ if (txsize != sizeof(pdu_header) + urb->actual_length) {
+ dev_err(&sdev->interface->dev,
+ "actual length of urb %d does not match iso packet sizes %zu\n",
+ urb->actual_length,
+ txsize-sizeof(pdu_header));
+ kfree(iov);
+ usbip_event_add(&sdev->ud,
+ SDEV_EVENT_ERROR_TCP);
+ return -1;
+ }
}
/* 3. setup iso_packet_descriptor */
@@ -211,47 +248,43 @@ static int stub_send_ret_submit(struct stub_device *sdev)
if (!iso_buffer) {
usbip_event_add(&sdev->ud,
SDEV_EVENT_ERROR_MALLOC);
+ kfree(iov);
return -1;
}
- iov[2].iov_base = iso_buffer;
- iov[2].iov_len = len;
+ iov[iovnum].iov_base = iso_buffer;
+ iov[iovnum].iov_len = len;
txsize += len;
+ iovnum++;
}
- ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
- 3, txsize);
+ ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
+ iov, iovnum, txsize);
if (ret != txsize) {
dev_err(&sdev->interface->dev,
"sendmsg failed!, retval %d for %zd\n",
ret, txsize);
+ kfree(iov);
kfree(iso_buffer);
usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
return -1;
}
+ kfree(iov);
kfree(iso_buffer);
- usbip_dbg_stub_tx("send txdata\n");
total_size += txsize;
}
-
spin_lock_irqsave(&sdev->priv_lock, flags);
-
list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) {
stub_free_priv_and_urb(priv);
}
-
spin_unlock_irqrestore(&sdev->priv_lock, flags);
return total_size;
}
-
-/*-------------------------------------------------------------------------*/
-/* send RET_UNLINK */
-
static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev)
{
unsigned long flags;
@@ -270,7 +303,6 @@ static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev)
return NULL;
}
-
static int stub_send_ret_unlink(struct stub_device *sdev)
{
unsigned long flags;
@@ -311,13 +343,10 @@ static int stub_send_ret_unlink(struct stub_device *sdev)
return -1;
}
-
usbip_dbg_stub_tx("send txdata\n");
-
total_size += txsize;
}
-
spin_lock_irqsave(&sdev->priv_lock, flags);
list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) {
@@ -330,20 +359,12 @@ static int stub_send_ret_unlink(struct stub_device *sdev)
return total_size;
}
-
-/*-------------------------------------------------------------------------*/
-
-void stub_tx_loop(struct usbip_task *ut)
+int stub_tx_loop(void *data)
{
- struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_tx);
+ struct usbip_device *ud = data;
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
- while (1) {
- if (signal_pending(current)) {
- usbip_dbg_stub_tx("signal catched\n");
- break;
- }
-
+ while (!kthread_should_stop()) {
if (usbip_event_happened(ud))
break;
@@ -368,7 +389,10 @@ void stub_tx_loop(struct usbip_task *ut)
break;
wait_event_interruptible(sdev->tx_waitq,
- (!list_empty(&sdev->priv_tx) ||
- !list_empty(&sdev->unlink_tx)));
+ (!list_empty(&sdev->priv_tx) ||
+ !list_empty(&sdev->unlink_tx) ||
+ kthread_should_stop()));
}
+
+ return 0;
}
diff --git a/drivers/staging/usbip/uapi/usbip.h b/drivers/staging/usbip/uapi/usbip.h
new file mode 100644
index 00000000000..fa5db30ede3
--- /dev/null
+++ b/drivers/staging/usbip/uapi/usbip.h
@@ -0,0 +1,26 @@
+/*
+ * usbip.h
+ *
+ * USBIP uapi defines and function prototypes etc.
+*/
+
+#ifndef _UAPI_LINUX_USBIP_H
+#define _UAPI_LINUX_USBIP_H
+
+/* usbip device status - exported in usbip device sysfs status */
+enum usbip_device_status {
+ /* sdev is available. */
+ SDEV_ST_AVAILABLE = 0x01,
+ /* sdev is now used. */
+ SDEV_ST_USED,
+ /* sdev is unusable because of a fatal error. */
+ SDEV_ST_ERROR,
+
+ /* vdev does not connect a remote device. */
+ VDEV_ST_NULL,
+ /* vdev is used, but the USB address is not assigned yet */
+ VDEV_ST_NOTASSIGNED,
+ VDEV_ST_USED,
+ VDEV_ST_ERROR
+};
+#endif /* _UAPI_LINUX_USBIP_H */
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index 210ef16bab8..facaaf003f1 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -17,87 +17,82 @@
* USA.
*/
-#include <linux/kernel.h>
-#include <linux/smp_lock.h>
+#include <asm/byteorder.h>
#include <linux/file.h>
-#include <linux/tcp.h>
-#include <linux/in.h>
-#include <linux/kthread.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
#include <linux/slab.h>
-#include "usbip_common.h"
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <net/sock.h>
-/* version information */
-#define DRIVER_VERSION "1.0"
-#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi _at_ users.sourceforge.net>"
-#define DRIVER_DESC "usbip common driver"
+#include "usbip_common.h"
-/*-------------------------------------------------------------------------*/
-/* debug routines */
+#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>"
+#define DRIVER_DESC "USB/IP Core"
-#ifdef CONFIG_USB_IP_DEBUG_ENABLE
+#ifdef CONFIG_USBIP_DEBUG
unsigned long usbip_debug_flag = 0xffffffff;
#else
unsigned long usbip_debug_flag;
#endif
EXPORT_SYMBOL_GPL(usbip_debug_flag);
-
+module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)");
/* FIXME */
struct device_attribute dev_attr_usbip_debug;
EXPORT_SYMBOL_GPL(dev_attr_usbip_debug);
-
-static ssize_t show_flag(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t usbip_debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%lx\n", usbip_debug_flag);
}
-static ssize_t store_flag(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t usbip_debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
- sscanf(buf, "%lx", &usbip_debug_flag);
-
+ if (sscanf(buf, "%lx", &usbip_debug_flag) != 1)
+ return -EINVAL;
return count;
}
-DEVICE_ATTR(usbip_debug, (S_IRUGO | S_IWUSR), show_flag, store_flag);
+DEVICE_ATTR_RW(usbip_debug);
static void usbip_dump_buffer(char *buff, int bufflen)
{
- print_hex_dump(KERN_DEBUG, "usb-ip", DUMP_PREFIX_OFFSET, 16, 4,
+ print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4,
buff, bufflen, false);
}
static void usbip_dump_pipe(unsigned int p)
{
unsigned char type = usb_pipetype(p);
- unsigned char ep = usb_pipeendpoint(p);
- unsigned char dev = usb_pipedevice(p);
- unsigned char dir = usb_pipein(p);
+ unsigned char ep = usb_pipeendpoint(p);
+ unsigned char dev = usb_pipedevice(p);
+ unsigned char dir = usb_pipein(p);
- printk(KERN_DEBUG "dev(%d) ", dev);
- printk(KERN_DEBUG "ep(%d) ", ep);
- printk(KERN_DEBUG "%s ", dir ? "IN" : "OUT");
+ pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT");
switch (type) {
case PIPE_ISOCHRONOUS:
- printk(KERN_DEBUG "%s ", "ISO");
+ pr_debug("ISO\n");
break;
case PIPE_INTERRUPT:
- printk(KERN_DEBUG "%s ", "INT");
+ pr_debug("INT\n");
break;
case PIPE_CONTROL:
- printk(KERN_DEBUG "%s ", "CTL");
+ pr_debug("CTRL\n");
break;
case PIPE_BULK:
- printk(KERN_DEBUG "%s ", "BLK");
+ pr_debug("BULK\n");
break;
default:
- printk(KERN_DEBUG "ERR");
+ pr_debug("ERR\n");
+ break;
}
-
- printk(KERN_DEBUG "\n");
-
}
static void usbip_dump_usb_device(struct usb_device *udev)
@@ -105,163 +100,137 @@ static void usbip_dump_usb_device(struct usb_device *udev)
struct device *dev = &udev->dev;
int i;
- dev_dbg(dev, " devnum(%d) devpath(%s)",
- udev->devnum, udev->devpath);
+ dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)",
+ udev->devnum, udev->devpath, usb_speed_string(udev->speed));
- switch (udev->speed) {
- case USB_SPEED_HIGH:
- printk(KERN_DEBUG " SPD_HIGH");
- break;
- case USB_SPEED_FULL:
- printk(KERN_DEBUG " SPD_FULL");
- break;
- case USB_SPEED_LOW:
- printk(KERN_DEBUG " SPD_LOW");
- break;
- case USB_SPEED_UNKNOWN:
- printk(KERN_DEBUG " SPD_UNKNOWN");
- break;
- default:
- printk(KERN_DEBUG " SPD_ERROR");
- }
-
- printk(KERN_DEBUG " tt %p, ttport %d", udev->tt, udev->ttport);
- printk(KERN_DEBUG "\n");
+ pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport);
dev_dbg(dev, " ");
for (i = 0; i < 16; i++)
- printk(KERN_DEBUG " %2u", i);
- printk(KERN_DEBUG "\n");
+ pr_debug(" %2u", i);
+ pr_debug("\n");
dev_dbg(dev, " toggle0(IN) :");
for (i = 0; i < 16; i++)
- printk(KERN_DEBUG " %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0);
- printk(KERN_DEBUG "\n");
+ pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0);
+ pr_debug("\n");
dev_dbg(dev, " toggle1(OUT):");
for (i = 0; i < 16; i++)
- printk(KERN_DEBUG " %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0);
- printk(KERN_DEBUG "\n");
-
+ pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0);
+ pr_debug("\n");
dev_dbg(dev, " epmaxp_in :");
for (i = 0; i < 16; i++) {
if (udev->ep_in[i])
- printk(KERN_DEBUG " %2u",
- le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize));
+ pr_debug(" %2u",
+ le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize));
}
- printk(KERN_DEBUG "\n");
+ pr_debug("\n");
dev_dbg(dev, " epmaxp_out :");
for (i = 0; i < 16; i++) {
if (udev->ep_out[i])
- printk(KERN_DEBUG " %2u",
- le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize));
+ pr_debug(" %2u",
+ le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize));
}
- printk(KERN_DEBUG "\n");
+ pr_debug("\n");
dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus);
- dev_dbg(dev, "descriptor %p, config %p, actconfig %p, "
- "rawdescriptors %p\n", &udev->descriptor, udev->config,
+ dev_dbg(dev,
+ "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n",
+ &udev->descriptor, udev->config,
udev->actconfig, udev->rawdescriptors);
dev_dbg(dev, "have_langid %d, string_langid %d\n",
udev->have_langid, udev->string_langid);
- dev_dbg(dev, "maxchild %d, children %p\n",
- udev->maxchild, udev->children);
+ dev_dbg(dev, "maxchild %d\n", udev->maxchild);
}
static void usbip_dump_request_type(__u8 rt)
{
switch (rt & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- printk(KERN_DEBUG "DEVICE");
+ pr_debug("DEVICE");
break;
case USB_RECIP_INTERFACE:
- printk(KERN_DEBUG "INTERF");
+ pr_debug("INTERF");
break;
case USB_RECIP_ENDPOINT:
- printk(KERN_DEBUG "ENDPOI");
+ pr_debug("ENDPOI");
break;
case USB_RECIP_OTHER:
- printk(KERN_DEBUG "OTHER ");
+ pr_debug("OTHER ");
break;
default:
- printk(KERN_DEBUG "------");
+ pr_debug("------");
+ break;
}
}
static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd)
{
if (!cmd) {
- printk(KERN_DEBUG " %s : null pointer\n", __func__);
+ pr_debug(" : null pointer\n");
return;
}
- printk(KERN_DEBUG " ");
- printk(KERN_DEBUG "bRequestType(%02X) ", cmd->bRequestType);
- printk(KERN_DEBUG "bRequest(%02X) " , cmd->bRequest);
- printk(KERN_DEBUG "wValue(%04X) ", cmd->wValue);
- printk(KERN_DEBUG "wIndex(%04X) ", cmd->wIndex);
- printk(KERN_DEBUG "wLength(%04X) ", cmd->wLength);
-
- printk(KERN_DEBUG "\n ");
+ pr_debug(" ");
+ pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ",
+ cmd->bRequestType, cmd->bRequest,
+ cmd->wValue, cmd->wIndex, cmd->wLength);
+ pr_debug("\n ");
if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
- printk(KERN_DEBUG "STANDARD ");
+ pr_debug("STANDARD ");
switch (cmd->bRequest) {
case USB_REQ_GET_STATUS:
- printk(KERN_DEBUG "GET_STATUS");
+ pr_debug("GET_STATUS\n");
break;
case USB_REQ_CLEAR_FEATURE:
- printk(KERN_DEBUG "CLEAR_FEAT");
+ pr_debug("CLEAR_FEAT\n");
break;
case USB_REQ_SET_FEATURE:
- printk(KERN_DEBUG "SET_FEAT ");
+ pr_debug("SET_FEAT\n");
break;
case USB_REQ_SET_ADDRESS:
- printk(KERN_DEBUG "SET_ADDRRS");
+ pr_debug("SET_ADDRRS\n");
break;
case USB_REQ_GET_DESCRIPTOR:
- printk(KERN_DEBUG "GET_DESCRI");
+ pr_debug("GET_DESCRI\n");
break;
case USB_REQ_SET_DESCRIPTOR:
- printk(KERN_DEBUG "SET_DESCRI");
+ pr_debug("SET_DESCRI\n");
break;
case USB_REQ_GET_CONFIGURATION:
- printk(KERN_DEBUG "GET_CONFIG");
+ pr_debug("GET_CONFIG\n");
break;
case USB_REQ_SET_CONFIGURATION:
- printk(KERN_DEBUG "SET_CONFIG");
+ pr_debug("SET_CONFIG\n");
break;
case USB_REQ_GET_INTERFACE:
- printk(KERN_DEBUG "GET_INTERF");
+ pr_debug("GET_INTERF\n");
break;
case USB_REQ_SET_INTERFACE:
- printk(KERN_DEBUG "SET_INTERF");
+ pr_debug("SET_INTERF\n");
break;
case USB_REQ_SYNCH_FRAME:
- printk(KERN_DEBUG "SYNC_FRAME");
+ pr_debug("SYNC_FRAME\n");
break;
default:
- printk(KERN_DEBUG "REQ(%02X) ", cmd->bRequest);
+ pr_debug("REQ(%02X)\n", cmd->bRequest);
+ break;
}
-
- printk(KERN_DEBUG " ");
usbip_dump_request_type(cmd->bRequestType);
-
- } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
- printk(KERN_DEBUG "CLASS ");
-
- else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR)
- printk(KERN_DEBUG "VENDOR ");
-
- else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED)
- printk(KERN_DEBUG "RESERVED");
-
- printk(KERN_DEBUG "\n");
+ } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+ pr_debug("CLASS\n");
+ } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
+ pr_debug("VENDOR\n");
+ } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) {
+ pr_debug("RESERVED\n");
+ }
}
void usbip_dump_urb(struct urb *urb)
@@ -269,16 +238,15 @@ void usbip_dump_urb(struct urb *urb)
struct device *dev;
if (!urb) {
- printk(KERN_DEBUG KBUILD_MODNAME
- ":%s: urb: null pointer!!\n", __func__);
+ pr_debug("urb: null pointer!!\n");
return;
}
if (!urb->dev) {
- printk(KERN_DEBUG KBUILD_MODNAME
- ":%s: urb->dev: null pointer!!\n", __func__);
+ pr_debug("urb->dev: null pointer!!\n");
return;
}
+
dev = &urb->dev->dev;
dev_dbg(dev, " urb :%p\n", urb);
@@ -299,7 +267,7 @@ void usbip_dump_urb(struct urb *urb)
dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet);
if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL)
- usbip_dump_usb_ctrlrequest(
+ usbip_dump_usb_ctrlrequest(
(struct usb_ctrlrequest *)urb->setup_packet);
dev_dbg(dev, " start_frame :%d\n", urb->start_frame);
@@ -313,152 +281,48 @@ EXPORT_SYMBOL_GPL(usbip_dump_urb);
void usbip_dump_header(struct usbip_header *pdu)
{
- usbip_udbg("BASE: cmd %u seq %u devid %u dir %u ep %u\n",
- pdu->base.command,
- pdu->base.seqnum,
- pdu->base.devid,
- pdu->base.direction,
- pdu->base.ep);
+ pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n",
+ pdu->base.command,
+ pdu->base.seqnum,
+ pdu->base.devid,
+ pdu->base.direction,
+ pdu->base.ep);
switch (pdu->base.command) {
case USBIP_CMD_SUBMIT:
- usbip_udbg("CMD_SUBMIT: "
- "x_flags %u x_len %u sf %u #p %u iv %u\n",
- pdu->u.cmd_submit.transfer_flags,
- pdu->u.cmd_submit.transfer_buffer_length,
- pdu->u.cmd_submit.start_frame,
- pdu->u.cmd_submit.number_of_packets,
- pdu->u.cmd_submit.interval);
- break;
+ pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n",
+ pdu->u.cmd_submit.transfer_flags,
+ pdu->u.cmd_submit.transfer_buffer_length,
+ pdu->u.cmd_submit.start_frame,
+ pdu->u.cmd_submit.number_of_packets,
+ pdu->u.cmd_submit.interval);
+ break;
case USBIP_CMD_UNLINK:
- usbip_udbg("CMD_UNLINK: seq %u\n", pdu->u.cmd_unlink.seqnum);
+ pr_debug("USBIP_CMD_UNLINK: seq %u\n",
+ pdu->u.cmd_unlink.seqnum);
break;
case USBIP_RET_SUBMIT:
- usbip_udbg("RET_SUBMIT: st %d al %u sf %d ec %d\n",
- pdu->u.ret_submit.status,
- pdu->u.ret_submit.actual_length,
- pdu->u.ret_submit.start_frame,
- pdu->u.ret_submit.error_count);
+ pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n",
+ pdu->u.ret_submit.status,
+ pdu->u.ret_submit.actual_length,
+ pdu->u.ret_submit.start_frame,
+ pdu->u.ret_submit.number_of_packets,
+ pdu->u.ret_submit.error_count);
+ break;
case USBIP_RET_UNLINK:
- usbip_udbg("RET_UNLINK: status %d\n", pdu->u.ret_unlink.status);
+ pr_debug("USBIP_RET_UNLINK: status %d\n",
+ pdu->u.ret_unlink.status);
break;
default:
/* NOT REACHED */
- usbip_udbg("UNKNOWN\n");
+ pr_err("unknown command\n");
+ break;
}
}
EXPORT_SYMBOL_GPL(usbip_dump_header);
-
-/*-------------------------------------------------------------------------*/
-/* thread routines */
-
-int usbip_thread(void *param)
-{
- struct usbip_task *ut = param;
-
- if (!ut)
- return -EINVAL;
-
- lock_kernel();
- daemonize(ut->name);
- allow_signal(SIGKILL);
- ut->thread = current;
- unlock_kernel();
-
- /* srv.rb must wait for rx_thread starting */
- complete(&ut->thread_done);
-
- /* start of while loop */
- ut->loop_ops(ut);
-
- /* end of loop */
- ut->thread = NULL;
-
- complete_and_exit(&ut->thread_done, 0);
-}
-
-static void stop_rx_thread(struct usbip_device *ud)
-{
- if (ud->tcp_rx.thread != NULL) {
- send_sig(SIGKILL, ud->tcp_rx.thread, 1);
- wait_for_completion(&ud->tcp_rx.thread_done);
- usbip_udbg("rx_thread for ud %p has finished\n", ud);
- }
-}
-
-static void stop_tx_thread(struct usbip_device *ud)
-{
- if (ud->tcp_tx.thread != NULL) {
- send_sig(SIGKILL, ud->tcp_tx.thread, 1);
- wait_for_completion(&ud->tcp_tx.thread_done);
- usbip_udbg("tx_thread for ud %p has finished\n", ud);
- }
-}
-
-int usbip_start_threads(struct usbip_device *ud)
-{
- /*
- * threads are invoked per one device (per one connection).
- */
- struct task_struct *th;
- int err = 0;
-
- th = kthread_run(usbip_thread, (void *)&ud->tcp_rx, "usbip");
- if (IS_ERR(th)) {
- printk(KERN_WARNING
- "Unable to start control thread\n");
- err = PTR_ERR(th);
- goto ust_exit;
- }
-
- th = kthread_run(usbip_thread, (void *)&ud->tcp_tx, "usbip");
- if (IS_ERR(th)) {
- printk(KERN_WARNING
- "Unable to start control thread\n");
- err = PTR_ERR(th);
- goto tx_thread_err;
- }
-
- /* confirm threads are starting */
- wait_for_completion(&ud->tcp_rx.thread_done);
- wait_for_completion(&ud->tcp_tx.thread_done);
-
- return 0;
-
-tx_thread_err:
- stop_rx_thread(ud);
-
-ust_exit:
- return err;
-}
-EXPORT_SYMBOL_GPL(usbip_start_threads);
-
-void usbip_stop_threads(struct usbip_device *ud)
-{
- /* kill threads related to this sdev, if v.c. exists */
- stop_rx_thread(ud);
- stop_tx_thread(ud);
-}
-EXPORT_SYMBOL_GPL(usbip_stop_threads);
-
-void usbip_task_init(struct usbip_task *ut, char *name,
- void (*loop_ops)(struct usbip_task *))
-{
- ut->thread = NULL;
- init_completion(&ut->thread_done);
- ut->name = name;
- ut->loop_ops = loop_ops;
-}
-EXPORT_SYMBOL_GPL(usbip_task_init);
-
-
-/*-------------------------------------------------------------------------*/
-/* socket routines */
-
-/* Send/receive messages over TCP/IP. I refer drivers/block/nbd.c */
-int usbip_xmit(int send, struct socket *sock, char *buf,
- int size, int msg_flags)
+/* Receive data over TCP/IP. */
+int usbip_recv(struct socket *sock, void *buf, int size)
{
int result;
struct msghdr msg;
@@ -472,27 +336,11 @@ int usbip_xmit(int send, struct socket *sock, char *buf,
usbip_dbg_xmit("enter\n");
if (!sock || !buf || !size) {
- printk(KERN_ERR "%s: invalid arg, sock %p buff %p size %d\n",
- __func__, sock, buf, size);
+ pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
+ size);
return -EINVAL;
}
-
- if (usbip_dbg_flag_xmit) {
- if (send) {
- if (!in_interrupt())
- printk(KERN_DEBUG "%-10s:", current->comm);
- else
- printk(KERN_DEBUG "interrupt :");
-
- printk(KERN_DEBUG "%s: sending... , sock %p, buf %p, "
- "size %d, msg_flags %d\n", __func__,
- sock, buf, size, msg_flags);
- usbip_dump_buffer(buf, size);
- }
- }
-
-
do {
sock->sk->sk_allocation = GFP_NOIO;
iov.iov_base = buf;
@@ -501,47 +349,30 @@ int usbip_xmit(int send, struct socket *sock, char *buf,
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
- msg.msg_namelen = 0;
- msg.msg_flags = msg_flags | MSG_NOSIGNAL;
-
- if (send)
- result = kernel_sendmsg(sock, &msg, &iov, 1, size);
- else
- result = kernel_recvmsg(sock, &msg, &iov, 1, size,
- MSG_WAITALL);
+ msg.msg_flags = MSG_NOSIGNAL;
+ result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
if (result <= 0) {
- usbip_udbg("usbip_xmit: %s sock %p buf %p size %u ret "
- "%d total %d\n",
- send ? "send" : "receive", sock, buf,
- size, result, total);
+ pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
+ sock, buf, size, result, total);
goto err;
}
size -= result;
buf += result;
total += result;
-
} while (size > 0);
-
if (usbip_dbg_flag_xmit) {
- if (!send) {
- if (!in_interrupt())
- printk(KERN_DEBUG "%-10s:", current->comm);
- else
- printk(KERN_DEBUG "interrupt :");
-
- printk(KERN_DEBUG "usbip_xmit: receiving....\n");
- usbip_dump_buffer(bp, osize);
- printk(KERN_DEBUG "usbip_xmit: received, osize %d ret "
- "%d size %d total %d\n", osize, result,
- size, total);
- }
+ if (!in_interrupt())
+ pr_debug("%-10s:", current->comm);
+ else
+ pr_debug("interrupt :");
- if (send)
- printk(KERN_DEBUG "usbip_xmit: send, total %d\n",
- total);
+ pr_debug("receiving....\n");
+ usbip_dump_buffer(bp, osize);
+ pr_debug("received, osize %d ret %d size %d total %d\n",
+ osize, result, size, total);
}
return total;
@@ -549,35 +380,7 @@ int usbip_xmit(int send, struct socket *sock, char *buf,
err:
return result;
}
-EXPORT_SYMBOL_GPL(usbip_xmit);
-
-struct socket *sockfd_to_socket(unsigned int sockfd)
-{
- struct socket *socket;
- struct file *file;
- struct inode *inode;
-
- file = fget(sockfd);
- if (!file) {
- printk(KERN_ERR "%s: invalid sockfd\n", __func__);
- return NULL;
- }
-
- inode = file->f_dentry->d_inode;
-
- if (!inode || !S_ISSOCK(inode->i_mode))
- return NULL;
-
- socket = SOCKET_I(inode);
-
- return socket;
-}
-EXPORT_SYMBOL_GPL(sockfd_to_socket);
-
-
-
-/*-------------------------------------------------------------------------*/
-/* pdu routines */
+EXPORT_SYMBOL_GPL(usbip_recv);
/* there may be more cases to tweak the flags. */
static unsigned int tweak_transfer_flags(unsigned int flags)
@@ -587,7 +390,7 @@ static unsigned int tweak_transfer_flags(unsigned int flags)
}
static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
- int pack)
+ int pack)
{
struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit;
@@ -596,17 +399,14 @@ static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
* will be discussed when usbip is ported to other operating systems.
*/
if (pack) {
- /* vhci_tx.c */
spdu->transfer_flags =
- tweak_transfer_flags(urb->transfer_flags);
+ tweak_transfer_flags(urb->transfer_flags);
spdu->transfer_buffer_length = urb->transfer_buffer_length;
spdu->start_frame = urb->start_frame;
spdu->number_of_packets = urb->number_of_packets;
spdu->interval = urb->interval;
} else {
- /* stub_rx.c */
urb->transfer_flags = spdu->transfer_flags;
-
urb->transfer_buffer_length = spdu->transfer_buffer_length;
urb->start_frame = spdu->start_frame;
urb->number_of_packets = spdu->number_of_packets;
@@ -615,30 +415,27 @@ static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
}
static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb,
- int pack)
+ int pack)
{
struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit;
if (pack) {
- /* stub_tx.c */
-
rpdu->status = urb->status;
rpdu->actual_length = urb->actual_length;
rpdu->start_frame = urb->start_frame;
+ rpdu->number_of_packets = urb->number_of_packets;
rpdu->error_count = urb->error_count;
} else {
- /* vhci_rx.c */
-
urb->status = rpdu->status;
urb->actual_length = rpdu->actual_length;
urb->start_frame = rpdu->start_frame;
+ urb->number_of_packets = rpdu->number_of_packets;
urb->error_count = rpdu->error_count;
}
}
-
void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
- int pack)
+ int pack)
{
switch (cmd) {
case USBIP_CMD_SUBMIT:
@@ -648,14 +445,13 @@ void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
usbip_pack_ret_submit(pdu, urb, pack);
break;
default:
- err("unknown command");
- /* NOTREACHED */
- /* BUG(); */
+ /* NOT REACHED */
+ pr_err("unknown command\n");
+ break;
}
}
EXPORT_SYMBOL_GPL(usbip_pack_pdu);
-
static void correct_endian_basic(struct usbip_header_basic *base, int send)
{
if (send) {
@@ -674,7 +470,7 @@ static void correct_endian_basic(struct usbip_header_basic *base, int send)
}
static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu,
- int send)
+ int send)
{
if (send) {
pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags);
@@ -694,23 +490,25 @@ static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu,
}
static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu,
- int send)
+ int send)
{
if (send) {
cpu_to_be32s(&pdu->status);
cpu_to_be32s(&pdu->actual_length);
cpu_to_be32s(&pdu->start_frame);
+ cpu_to_be32s(&pdu->number_of_packets);
cpu_to_be32s(&pdu->error_count);
} else {
be32_to_cpus(&pdu->status);
be32_to_cpus(&pdu->actual_length);
be32_to_cpus(&pdu->start_frame);
+ be32_to_cpus(&pdu->number_of_packets);
be32_to_cpus(&pdu->error_count);
}
}
static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu,
- int send)
+ int send)
{
if (send)
pdu->seqnum = cpu_to_be32(pdu->seqnum);
@@ -719,7 +517,7 @@ static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu,
}
static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu,
- int send)
+ int send)
{
if (send)
cpu_to_be32s(&pdu->status);
@@ -753,16 +551,15 @@ void usbip_header_correct_endian(struct usbip_header *pdu, int send)
correct_endian_ret_unlink(&pdu->u.ret_unlink, send);
break;
default:
- /* NOTREACHED */
- err("unknown command in pdu header: %d", cmd);
- /* BUG(); */
+ /* NOT REACHED */
+ pr_err("unknown command\n");
+ break;
}
}
EXPORT_SYMBOL_GPL(usbip_header_correct_endian);
-static void usbip_iso_pakcet_correct_endian(
- struct usbip_iso_packet_descriptor *iso,
- int send)
+static void usbip_iso_packet_correct_endian(
+ struct usbip_iso_packet_descriptor *iso, int send)
{
/* does not need all members. but copy all simply. */
if (send) {
@@ -779,7 +576,7 @@ static void usbip_iso_pakcet_correct_endian(
}
static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
- struct usb_iso_packet_descriptor *uiso, int pack)
+ struct usb_iso_packet_descriptor *uiso, int pack)
{
if (pack) {
iso->offset = uiso->offset;
@@ -794,30 +591,27 @@ static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
}
}
-
/* must free buffer */
-void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen)
+struct usbip_iso_packet_descriptor*
+usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen)
{
- void *buff;
struct usbip_iso_packet_descriptor *iso;
int np = urb->number_of_packets;
ssize_t size = np * sizeof(*iso);
int i;
- buff = kzalloc(size, GFP_KERNEL);
- if (!buff)
+ iso = kzalloc(size, GFP_KERNEL);
+ if (!iso)
return NULL;
for (i = 0; i < np; i++) {
- iso = buff + (i * sizeof(*iso));
-
- usbip_pack_iso(iso, &urb->iso_frame_desc[i], 1);
- usbip_iso_pakcet_correct_endian(iso, 1);
+ usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1);
+ usbip_iso_packet_correct_endian(&iso[i], 1);
}
*bufflen = size;
- return buff;
+ return iso;
}
EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu);
@@ -830,22 +624,20 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
int size = np * sizeof(*iso);
int i;
int ret;
+ int total_length = 0;
if (!usb_pipeisoc(urb->pipe))
return 0;
/* my Bluetooth dongle gets ISO URBs which are np = 0 */
- if (np == 0) {
- /* usbip_uinfo("iso np == 0\n"); */
- /* usbip_dump_urb(urb); */
+ if (np == 0)
return 0;
- }
buff = kzalloc(size, GFP_KERNEL);
if (!buff)
return -ENOMEM;
- ret = usbip_xmit(0, ud->tcp_socket, buff, size, 0);
+ ret = usbip_recv(ud->tcp_socket, buff, size);
if (ret != size) {
dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n",
ret);
@@ -859,19 +651,71 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
return -EPIPE;
}
+ iso = (struct usbip_iso_packet_descriptor *) buff;
for (i = 0; i < np; i++) {
- iso = buff + (i * sizeof(*iso));
-
- usbip_iso_pakcet_correct_endian(iso, 0);
- usbip_pack_iso(iso, &urb->iso_frame_desc[i], 0);
+ usbip_iso_packet_correct_endian(&iso[i], 0);
+ usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0);
+ total_length += urb->iso_frame_desc[i].actual_length;
}
kfree(buff);
+ if (total_length != urb->actual_length) {
+ dev_err(&urb->dev->dev,
+ "total length of iso packets %d not equal to actual length of buffer %d\n",
+ total_length, urb->actual_length);
+
+ if (ud->side == USBIP_STUB)
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ else
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+
+ return -EPIPE;
+ }
+
return ret;
}
EXPORT_SYMBOL_GPL(usbip_recv_iso);
+/*
+ * This functions restores the padding which was removed for optimizing
+ * the bandwidth during transfer over tcp/ip
+ *
+ * buffer and iso packets need to be stored and be in propeper endian in urb
+ * before calling this function
+ */
+void usbip_pad_iso(struct usbip_device *ud, struct urb *urb)
+{
+ int np = urb->number_of_packets;
+ int i;
+ int actualoffset = urb->actual_length;
+
+ if (!usb_pipeisoc(urb->pipe))
+ return;
+
+ /* if no packets or length of data is 0, then nothing to unpack */
+ if (np == 0 || urb->actual_length == 0)
+ return;
+
+ /*
+ * if actual_length is transfer_buffer_length then no padding is
+ * present.
+ */
+ if (urb->actual_length == urb->transfer_buffer_length)
+ return;
+
+ /*
+ * loop over all packets from last to first (to prevent overwritting
+ * memory when padding) and move them into the proper place
+ */
+ for (i = np-1; i > 0; i--) {
+ actualoffset -= urb->iso_frame_desc[i].actual_length;
+ memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset,
+ urb->transfer_buffer + actualoffset,
+ urb->iso_frame_desc[i].actual_length);
+ }
+}
+EXPORT_SYMBOL_GPL(usbip_pad_iso);
/* some members of urb must be substituted before. */
int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
@@ -880,14 +724,12 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
int size;
if (ud->side == USBIP_STUB) {
- /* stub_rx.c */
/* the direction of urb must be OUT. */
if (usb_pipein(urb->pipe))
return 0;
size = urb->transfer_buffer_length;
} else {
- /* vhci_rx.c */
/* the direction of urb must be IN. */
if (usb_pipeout(urb->pipe))
return 0;
@@ -899,8 +741,7 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
if (!(size > 0))
return 0;
- ret = usbip_xmit(0, ud->tcp_socket, (char *)urb->transfer_buffer,
- size, 0);
+ ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
if (ret != size) {
dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
if (ud->side == USBIP_STUB) {
@@ -915,27 +756,21 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
}
EXPORT_SYMBOL_GPL(usbip_recv_xbuff);
-
-/*-------------------------------------------------------------------------*/
-
-static int __init usbip_common_init(void)
+static int __init usbip_core_init(void)
{
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "" DRIVER_VERSION);
-
+ pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
return 0;
}
-static void __exit usbip_common_exit(void)
+static void __exit usbip_core_exit(void)
{
return;
}
-
-
-
-module_init(usbip_common_init);
-module_exit(usbip_common_exit);
+module_init(usbip_core_init);
+module_exit(usbip_core_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_VERSION(USBIP_VERSION);
diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
index d280e234e06..4da3866a037 100644
--- a/drivers/staging/usbip/usbip_common.h
+++ b/drivers/staging/usbip/usbip_common.h
@@ -17,42 +17,29 @@
* USA.
*/
-#ifndef __VHCI_COMMON_H
-#define __VHCI_COMMON_H
-
-
-#include <linux/version.h>
+#ifndef __USBIP_COMMON_H
+#define __USBIP_COMMON_H
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/net.h>
+#include <linux/printk.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
#include <linux/usb.h>
-#include <asm/byteorder.h>
-#include <net/sock.h>
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * define macros to print messages
- */
+#include <linux/wait.h>
+#include "uapi/usbip.h"
-/**
- * usbip_udbg - print debug messages if CONFIG_USB_IP_DEBUG_ENABLE is defined
- * @fmt:
- * @args:
- */
+#define USBIP_VERSION "1.0.0"
-#ifdef CONFIG_USB_IP_DEBUG_ENABLE
-
-#define usbip_udbg(fmt, args...) \
- do { \
- printk(KERN_DEBUG "%-10s:(%s,%d) %s: " fmt, \
- (in_interrupt() ? "interrupt" : (current)->comm),\
- __FILE__, __LINE__, __func__, ##args); \
- } while (0)
-
-#else /* CONFIG_USB_IP_DEBUG_ENABLE */
-
-#define usbip_udbg(fmt, args...) do { } while (0)
-
-#endif /* CONFIG_USB_IP_DEBUG_ENABLE */
+#undef pr_fmt
+#ifdef DEBUG
+#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__
+#else
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#endif
enum {
usbip_debug_xmit = (1 << 0),
@@ -79,7 +66,7 @@ enum {
#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx)
#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_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs)
extern unsigned long usbip_debug_flag;
extern struct device_attribute dev_attr_usbip_debug;
@@ -87,16 +74,16 @@ extern struct device_attribute dev_attr_usbip_debug;
#define usbip_dbg_with_flag(flag, fmt, args...) \
do { \
if (flag & usbip_debug_flag) \
- usbip_udbg(fmt , ##args); \
+ pr_debug(fmt, ##args); \
} while (0)
-#define usbip_dbg_sysfs(fmt, args...) \
+#define usbip_dbg_sysfs(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args)
-#define usbip_dbg_xmit(fmt, args...) \
+#define usbip_dbg_xmit(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args)
-#define usbip_dbg_urb(fmt, args...) \
+#define usbip_dbg_urb(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args)
-#define usbip_dbg_eh(fmt, args...) \
+#define usbip_dbg_eh(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args)
#define usbip_dbg_vhci_rh(fmt, args...) \
@@ -107,151 +94,121 @@ extern struct device_attribute dev_attr_usbip_debug;
usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args)
#define usbip_dbg_vhci_tx(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args)
-#define usbip_dbg_vhci_sysfs(fmt, args...) \
+#define usbip_dbg_vhci_sysfs(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args)
-#define usbip_dbg_stub_cmp(fmt, args...) \
+#define usbip_dbg_stub_cmp(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args)
-#define usbip_dbg_stub_rx(fmt, args...) \
+#define usbip_dbg_stub_rx(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args)
-#define usbip_dbg_stub_tx(fmt, args...) \
+#define usbip_dbg_stub_tx(fmt, args...) \
usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args)
-
-/**
- * usbip_uerr - print error messages
- * @fmt:
- * @args:
- */
-#define usbip_uerr(fmt, args...) \
- do { \
- printk(KERN_ERR "%-10s: ***ERROR*** (%s,%d) %s: " fmt, \
- (in_interrupt() ? "interrupt" : (current)->comm),\
- __FILE__, __LINE__, __func__, ##args); \
- } while (0)
-
-/**
- * usbip_uinfo - print information messages
- * @fmt:
- * @args:
- */
-#define usbip_uinfo(fmt, args...) \
- do { \
- printk(KERN_INFO "usbip: " fmt , ## args); \
- } while (0)
-
-
-/*-------------------------------------------------------------------------*/
-
/*
- * USB/IP request headers.
- * Currently, we define 4 request types:
+ * USB/IP request headers
*
- * - CMD_SUBMIT transfers a USB request, corresponding to usb_submit_urb().
- * (client to server)
- * - RET_RETURN transfers the result of CMD_SUBMIT.
- * (server to client)
- * - CMD_UNLINK transfers an unlink request of a pending USB request.
+ * Each request is transferred across the network to its counterpart, which
+ * facilitates the normal USB communication. The values contained in the headers
+ * are basically the same as in a URB. Currently, four request types are
+ * defined:
+ *
+ * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb()
* (client to server)
- * - RET_UNLINK transfers the result of CMD_UNLINK.
+ *
+ * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT
* (server to client)
*
- * Note: The below request formats are based on the USB subsystem of Linux. Its
- * details will be defined when other implementations come.
+ * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT,
+ * corresponds to usb_unlink_urb()
+ * (client to server)
*
+ * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK
+ * (server to client)
*
*/
-
-/*
- * A basic header followed by other additional headers.
- */
-struct usbip_header_basic {
#define USBIP_CMD_SUBMIT 0x0001
#define USBIP_CMD_UNLINK 0x0002
#define USBIP_RET_SUBMIT 0x0003
#define USBIP_RET_UNLINK 0x0004
- __u32 command;
- /* sequential number which identifies requests.
- * incremented per connections */
- __u32 seqnum;
+#define USBIP_DIR_OUT 0x00
+#define USBIP_DIR_IN 0x01
- /* devid is used to specify a remote USB device uniquely instead
- * of busnum and devnum in Linux. In the case of Linux stub_driver,
- * this value is ((busnum << 16) | devnum) */
+/**
+ * struct usbip_header_basic - data pertinent to every request
+ * @command: the usbip request type
+ * @seqnum: sequential number that identifies requests; incremented per
+ * connection
+ * @devid: specifies a remote USB device uniquely instead of busnum and devnum;
+ * in the stub driver, this value is ((busnum << 16) | devnum)
+ * @direction: direction of the transfer
+ * @ep: endpoint number
+ */
+struct usbip_header_basic {
+ __u32 command;
+ __u32 seqnum;
__u32 devid;
-
-#define USBIP_DIR_OUT 0
-#define USBIP_DIR_IN 1
__u32 direction;
- __u32 ep; /* endpoint number */
-} __attribute__ ((packed));
+ __u32 ep;
+} __packed;
-/*
- * An additional header for a CMD_SUBMIT packet.
+/**
+ * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header
+ * @transfer_flags: URB flags
+ * @transfer_buffer_length: the data size for (in) or (out) transfer
+ * @start_frame: initial frame for isochronous or interrupt transfers
+ * @number_of_packets: number of isochronous packets
+ * @interval: maximum time for the request on the server-side host controller
+ * @setup: setup data for a control request
*/
struct usbip_header_cmd_submit {
- /* these values are basically the same as in a URB. */
-
- /* the same in a URB. */
__u32 transfer_flags;
-
- /* set the following data size (out),
- * or expected reading data size (in) */
__s32 transfer_buffer_length;
/* it is difficult for usbip to sync frames (reserved only?) */
__s32 start_frame;
-
- /* the number of iso descriptors that follows this header */
__s32 number_of_packets;
-
- /* the maximum time within which this request works in a host
- * controller of a server side */
__s32 interval;
- /* set setup packet data for a CTRL request */
unsigned char setup[8];
-} __attribute__ ((packed));
+} __packed;
-/*
- * An additional header for a RET_SUBMIT packet.
+/**
+ * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header
+ * @status: return status of a non-iso request
+ * @actual_length: number of bytes transferred
+ * @start_frame: initial frame for isochronous or interrupt transfers
+ * @number_of_packets: number of isochronous packets
+ * @error_count: number of errors for isochronous transfers
*/
struct usbip_header_ret_submit {
__s32 status;
- __s32 actual_length; /* returned data length */
- __s32 start_frame; /* ISO and INT */
- __s32 number_of_packets; /* ISO only */
- __s32 error_count; /* ISO only */
-} __attribute__ ((packed));
+ __s32 actual_length;
+ __s32 start_frame;
+ __s32 number_of_packets;
+ __s32 error_count;
+} __packed;
-/*
- * An additional header for a CMD_UNLINK packet.
+/**
+ * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header
+ * @seqnum: the URB seqnum to unlink
*/
struct usbip_header_cmd_unlink {
- __u32 seqnum; /* URB's seqnum which will be unlinked */
-} __attribute__ ((packed));
-
+ __u32 seqnum;
+} __packed;
-/*
- * An additional header for a RET_UNLINK packet.
+/**
+ * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header
+ * @status: return status of the request
*/
struct usbip_header_ret_unlink {
__s32 status;
-} __attribute__ ((packed));
-
-
-/* the same as usb_iso_packet_descriptor but packed for pdu */
-struct usbip_iso_packet_descriptor {
- __u32 offset;
- __u32 length; /* expected length */
- __u32 actual_length;
- __u32 status;
-} __attribute__ ((packed));
+} __packed;
-
-/*
- * All usbip packets use a common header to keep code simple.
+/**
+ * struct usbip_header - common header for all usbip packets
+ * @base: the basic header
+ * @u: packet type dependent header
*/
struct usbip_header {
struct usbip_header_basic base;
@@ -262,94 +219,24 @@ struct usbip_header {
struct usbip_header_cmd_unlink cmd_unlink;
struct usbip_header_ret_unlink ret_unlink;
} u;
-} __attribute__ ((packed));
-
-
-
-
-/*-------------------------------------------------------------------------*/
-
-
-int usbip_xmit(int, struct socket *, char *, int, int);
-int usbip_sendmsg(struct socket *, struct msghdr *, int);
-
-
-static inline int interface_to_busnum(struct usb_interface *interface)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- return udev->bus->busnum;
-}
-
-static inline int interface_to_devnum(struct usb_interface *interface)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- return udev->devnum;
-}
-
-static inline int interface_to_infnum(struct usb_interface *interface)
-{
- return interface->cur_altsetting->desc.bInterfaceNumber;
-}
-
-#if 0
-int setnodelay(struct socket *);
-int setquickack(struct socket *);
-int setkeepalive(struct socket *socket);
-void setreuse(struct socket *);
-#endif
-
-struct socket *sockfd_to_socket(unsigned int);
-int set_sockaddr(struct socket *socket, struct sockaddr_storage *ss);
-
-void usbip_dump_urb(struct urb *purb);
-void usbip_dump_header(struct usbip_header *pdu);
-
-
-struct usbip_device;
+} __packed;
-struct usbip_task {
- struct task_struct *thread;
- struct completion thread_done;
- char *name;
- void (*loop_ops)(struct usbip_task *);
-};
+/*
+ * This is the same as usb_iso_packet_descriptor but packed for pdu.
+ */
+struct usbip_iso_packet_descriptor {
+ __u32 offset;
+ __u32 length; /* expected length */
+ __u32 actual_length;
+ __u32 status;
+} __packed;
enum usbip_side {
USBIP_VHCI,
USBIP_STUB,
};
-enum usbip_status {
- /* sdev is available. */
- SDEV_ST_AVAILABLE = 0x01,
- /* sdev is now used. */
- SDEV_ST_USED,
- /* sdev is unusable because of a fatal error. */
- SDEV_ST_ERROR,
-
- /* vdev does not connect a remote device. */
- VDEV_ST_NULL,
- /* vdev is used, but the USB address is not assigned yet */
- VDEV_ST_NOTASSIGNED,
- VDEV_ST_USED,
- VDEV_ST_ERROR
-};
-
-/* a common structure for stub_device and vhci_device */
-struct usbip_device {
- enum usbip_side side;
-
- enum usbip_status status;
-
- /* lock for status */
- spinlock_t lock;
-
- struct socket *tcp_socket;
-
- struct usbip_task tcp_rx;
- struct usbip_task tcp_tx;
-
- /* event handler */
+/* event handler */
#define USBIP_EH_SHUTDOWN (1 << 0)
#define USBIP_EH_BYE (1 << 1)
#define USBIP_EH_RESET (1 << 2)
@@ -366,8 +253,21 @@ struct usbip_device {
#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
+/* a common structure for stub_device and vhci_device */
+struct usbip_device {
+ enum usbip_side side;
+ enum usbip_device_status status;
+
+ /* lock for status */
+ spinlock_t lock;
+
+ struct socket *tcp_socket;
+
+ struct task_struct *tcp_rx;
+ struct task_struct *tcp_tx;
+
unsigned long event;
- struct usbip_task eh;
+ struct task_struct *eh;
wait_queue_head_t eh_waitq;
struct eh_ops {
@@ -377,24 +277,40 @@ struct usbip_device {
} eh_ops;
};
+#define kthread_get_run(threadfn, data, namefmt, ...) \
+({ \
+ struct task_struct *__k \
+ = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
+ if (!IS_ERR(__k)) { \
+ get_task_struct(__k); \
+ wake_up_process(__k); \
+ } \
+ __k; \
+})
+
+#define kthread_stop_put(k) \
+ do { \
+ kthread_stop(k); \
+ put_task_struct(k); \
+ } while (0)
-void usbip_task_init(struct usbip_task *ut, char *,
- void (*loop_ops)(struct usbip_task *));
+/* usbip_common.c */
+void usbip_dump_urb(struct urb *purb);
+void usbip_dump_header(struct usbip_header *pdu);
-int usbip_start_threads(struct usbip_device *ud);
-void usbip_stop_threads(struct usbip_device *ud);
-int usbip_thread(void *param);
+int usbip_recv(struct socket *sock, void *buf, int size);
void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
- int pack);
-
+ int pack);
void usbip_header_correct_endian(struct usbip_header *pdu, int send);
-/* some members of urb must be substituted before. */
-int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb);
+
+struct usbip_iso_packet_descriptor*
+usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen);
+
/* some members of urb must be substituted before. */
int usbip_recv_iso(struct usbip_device *ud, struct urb *urb);
-void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen);
-
+void usbip_pad_iso(struct usbip_device *ud, struct urb *urb);
+int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb);
/* usbip_event.c */
int usbip_start_eh(struct usbip_device *ud);
@@ -402,5 +318,18 @@ void usbip_stop_eh(struct usbip_device *ud);
void usbip_event_add(struct usbip_device *ud, unsigned long event);
int usbip_event_happened(struct usbip_device *ud);
+static inline int interface_to_busnum(struct usb_interface *interface)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
-#endif
+ return udev->bus->busnum;
+}
+
+static inline int interface_to_devnum(struct usb_interface *interface)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+
+ return udev->devnum;
+}
+
+#endif /* __USBIP_COMMON_H */
diff --git a/drivers/staging/usbip/usbip_event.c b/drivers/staging/usbip/usbip_event.c
index af3832b03e4..64933b993d7 100644
--- a/drivers/staging/usbip/usbip_event.c
+++ b/drivers/staging/usbip/usbip_event.c
@@ -17,8 +17,10 @@
* USA.
*/
-#include "usbip_common.h"
#include <linux/kthread.h>
+#include <linux/export.h>
+
+#include "usbip_common.h"
static int event_handler(struct usbip_device *ud)
{
@@ -36,21 +38,18 @@ static int event_handler(struct usbip_device *ud)
*/
if (ud->event & USBIP_EH_SHUTDOWN) {
ud->eh_ops.shutdown(ud);
-
ud->event &= ~USBIP_EH_SHUTDOWN;
}
/* Reset the device. */
if (ud->event & USBIP_EH_RESET) {
ud->eh_ops.reset(ud);
-
ud->event &= ~USBIP_EH_RESET;
}
/* Mark the device as unusable. */
if (ud->event & USBIP_EH_UNUSABLE) {
ud->eh_ops.unusable(ud);
-
ud->event &= ~USBIP_EH_UNUSABLE;
}
@@ -62,68 +61,56 @@ static int event_handler(struct usbip_device *ud)
return 0;
}
-static void event_handler_loop(struct usbip_task *ut)
+static int event_handler_loop(void *data)
{
- struct usbip_device *ud = container_of(ut, struct usbip_device, eh);
+ struct usbip_device *ud = data;
- while (1) {
- if (signal_pending(current)) {
- usbip_dbg_eh("signal catched!\n");
- break;
- }
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(ud->eh_waitq,
+ usbip_event_happened(ud) ||
+ kthread_should_stop());
+ usbip_dbg_eh("wakeup\n");
if (event_handler(ud) < 0)
break;
-
- wait_event_interruptible(ud->eh_waitq,
- usbip_event_happened(ud));
- usbip_dbg_eh("wakeup\n");
}
+
+ return 0;
}
int usbip_start_eh(struct usbip_device *ud)
{
- struct usbip_task *eh = &ud->eh;
- struct task_struct *th;
-
init_waitqueue_head(&ud->eh_waitq);
ud->event = 0;
- usbip_task_init(eh, "usbip_eh", event_handler_loop);
-
- th = kthread_run(usbip_thread, (void *)eh, "usbip");
- if (IS_ERR(th)) {
- printk(KERN_WARNING
- "Unable to start control thread\n");
- return PTR_ERR(th);
+ ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh");
+ if (IS_ERR(ud->eh)) {
+ pr_warn("Unable to start control thread\n");
+ return PTR_ERR(ud->eh);
}
- wait_for_completion(&eh->thread_done);
return 0;
}
EXPORT_SYMBOL_GPL(usbip_start_eh);
void usbip_stop_eh(struct usbip_device *ud)
{
- struct usbip_task *eh = &ud->eh;
-
- if (eh->thread == current)
+ if (ud->eh == current)
return; /* do not wait for myself */
- wait_for_completion(&eh->thread_done);
+ kthread_stop(ud->eh);
usbip_dbg_eh("usbip_eh has finished\n");
}
EXPORT_SYMBOL_GPL(usbip_stop_eh);
void usbip_event_add(struct usbip_device *ud, unsigned long event)
{
- spin_lock(&ud->lock);
+ unsigned long flags;
+ spin_lock_irqsave(&ud->lock, flags);
ud->event |= event;
-
wake_up(&ud->eh_waitq);
-
- spin_unlock(&ud->lock);
+ spin_unlock_irqrestore(&ud->lock, flags);
}
EXPORT_SYMBOL_GPL(usbip_event_add);
@@ -132,10 +119,8 @@ int usbip_event_happened(struct usbip_device *ud)
int happened = 0;
spin_lock(&ud->lock);
-
if (ud->event != 0)
happened = 1;
-
spin_unlock(&ud->lock);
return happened;
diff --git a/drivers/staging/usbip/usbip_protocol.txt b/drivers/staging/usbip/usbip_protocol.txt
new file mode 100644
index 00000000000..16b6fe27284
--- /dev/null
+++ b/drivers/staging/usbip/usbip_protocol.txt
@@ -0,0 +1,358 @@
+PRELIMINARY DRAFT, MAY CONTAIN MISTAKES!
+28 Jun 2011
+
+The USB/IP protocol follows a server/client architecture. The server exports the
+USB devices and the clients imports them. The device driver for the exported
+USB device runs on the client machine.
+
+The client may ask for the list of the exported USB devices. To get the list the
+client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST
+packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent
+in one or more pieces at the low level transport layer). The server sends back
+the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the
+TCP/IP connection is closed.
+
+ virtual host controller usb host
+ "client" "server"
+ (imports USB devices) (exports USB devices)
+ | |
+ | OP_REQ_DEVLIST |
+ | ----------------------------------------------> |
+ | |
+ | OP_REP_DEVLIST |
+ | <---------------------------------------------- |
+ | |
+
+Once the client knows the list of exported USB devices it may decide to use one
+of them. First the client opens a TCP/IP connection towards the server and
+sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the
+import was successful the TCP/IP connection remains open and will be used
+to transfer the URB traffic between the client and the server. The client may
+send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and
+USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the
+server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively.
+
+ virtual host controller usb host
+ "client" "server"
+ (imports USB devices) (exports USB devices)
+ | |
+ | OP_REQ_IMPORT |
+ | ----------------------------------------------> |
+ | |
+ | OP_REP_IMPORT |
+ | <---------------------------------------------- |
+ | |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = n) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = n) |
+ | <---------------------------------------------- |
+ | . |
+ | : |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+1) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+2) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = m) |
+ | <---------------------------------------------- |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+3) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = m+1) |
+ | <---------------------------------------------- |
+ | |
+ | USBIP_CMD_SUBMIT(seqnum = m+4) |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_SUBMIT(seqnum = m+2) |
+ | <---------------------------------------------- |
+ | . |
+ | : |
+ | |
+ | USBIP_CMD_UNLINK |
+ | ----------------------------------------------> |
+ | |
+ | USBIP_RET_UNLINK |
+ | <---------------------------------------------- |
+ | |
+
+The fields are in network (big endian) byte order meaning that the most significant
+byte (MSB) is stored at the lowest address.
+
+
+OP_REQ_DEVLIST: Retrieve the list of exported USB devices.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB
+ | | | devices.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: unused, shall be set to 0
+
+OP_REP_DEVLIST: Reply with the list of exported USB devices.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x0005 | Reply code: The list of exported USB devices.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: 0 for OK
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | n | Number of exported devices: 0 means no exported
+ | | | devices.
+-----------+--------+------------+---------------------------------------------------
+ 0x0C | | | From now on the exported n devices are described,
+ | | | if any. If no devices are exported the message
+ | | | ends with the previous "number of exported
+ | | | devices" field.
+-----------+--------+------------+---------------------------------------------------
+ | 256 | | path: Path of the device on the host exporting the
+ | | | USB device, string closed with zero byte, e.g.
+ | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
+ | | | The unused bytes shall be filled with zero
+ | | | bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x10C | 32 | | busid: Bus ID of the exported device, string
+ | | | closed with zero byte, e.g. "3-2". The unused
+ | | | bytes shall be filled with zero bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x12C | 4 | | busnum
+-----------+--------+------------+---------------------------------------------------
+ 0x130 | 4 | | devnum
+-----------+--------+------------+---------------------------------------------------
+ 0x134 | 4 | | speed
+-----------+--------+------------+---------------------------------------------------
+ 0x138 | 2 | | idVendor
+-----------+--------+------------+---------------------------------------------------
+ 0x13A | 2 | | idProduct
+-----------+--------+------------+---------------------------------------------------
+ 0x13C | 2 | | bcdDevice
+-----------+--------+------------+---------------------------------------------------
+ 0x13E | 1 | | bDeviceClass
+-----------+--------+------------+---------------------------------------------------
+ 0x13F | 1 | | bDeviceSubClass
+-----------+--------+------------+---------------------------------------------------
+ 0x140 | 1 | | bDeviceProtocol
+-----------+--------+------------+---------------------------------------------------
+ 0x141 | 1 | | bConfigurationValue
+-----------+--------+------------+---------------------------------------------------
+ 0x142 | 1 | | bNumConfigurations
+-----------+--------+------------+---------------------------------------------------
+ 0x143 | 1 | | bNumInterfaces
+-----------+--------+------------+---------------------------------------------------
+ 0x144 | | m_0 | From now on each interface is described, all
+ | | | together bNumInterfaces times, with the
+ | | | the following 4 fields:
+-----------+--------+------------+---------------------------------------------------
+ | 1 | | bInterfaceClass
+-----------+--------+------------+---------------------------------------------------
+ 0x145 | 1 | | bInterfaceSubClass
+-----------+--------+------------+---------------------------------------------------
+ 0x146 | 1 | | bInterfaceProtocol
+-----------+--------+------------+---------------------------------------------------
+ 0x147 | 1 | | padding byte for alignment, shall be set to zero
+-----------+--------+------------+---------------------------------------------------
+ 0xC + | | | The second exported USB device starts at i=1
+ i*0x138 + | | | with the busid field.
+ m_(i-1)*4 | | |
+
+OP_REQ_IMPORT: Request to import (attach) a remote USB device.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x8003 | Command code: import a remote USB device.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: unused, shall be set to 0
+-----------+--------+------------+---------------------------------------------------
+ 8 | 32 | | busid: the busid of the exported device on the
+ | | | remote host. The possible values are taken
+ | | | from the message field OP_REP_DEVLIST.busid.
+ | | | A string closed with zero, the unused bytes
+ | | | shall be filled with zeros.
+-----------+--------+------------+---------------------------------------------------
+
+OP_REP_IMPORT: Reply to import (attach) a remote USB device.
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
+-----------+--------+------------+---------------------------------------------------
+ 2 | 2 | 0x0003 | Reply code: Reply to import.
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | 0x00000000 | Status: 0 for OK
+ | | | 1 for error
+-----------+--------+------------+---------------------------------------------------
+ 8 | | | From now on comes the details of the imported
+ | | | device, if the previous status field was OK (0),
+ | | | otherwise the reply ends with the status field.
+-----------+--------+------------+---------------------------------------------------
+ | 256 | | path: Path of the device on the host exporting the
+ | | | USB device, string closed with zero byte, e.g.
+ | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
+ | | | The unused bytes shall be filled with zero
+ | | | bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x108 | 32 | | busid: Bus ID of the exported device, string
+ | | | closed with zero byte, e.g. "3-2". The unused
+ | | | bytes shall be filled with zero bytes.
+-----------+--------+------------+---------------------------------------------------
+ 0x128 | 4 | | busnum
+-----------+--------+------------+---------------------------------------------------
+ 0x12C | 4 | | devnum
+-----------+--------+------------+---------------------------------------------------
+ 0x130 | 4 | | speed
+-----------+--------+------------+---------------------------------------------------
+ 0x134 | 2 | | idVendor
+-----------+--------+------------+---------------------------------------------------
+ 0x136 | 2 | | idProduct
+-----------+--------+------------+---------------------------------------------------
+ 0x138 | 2 | | bcdDevice
+-----------+--------+------------+---------------------------------------------------
+ 0x139 | 1 | | bDeviceClass
+-----------+--------+------------+---------------------------------------------------
+ 0x13A | 1 | | bDeviceSubClass
+-----------+--------+------------+---------------------------------------------------
+ 0x13B | 1 | | bDeviceProtocol
+-----------+--------+------------+---------------------------------------------------
+ 0x13C | 1 | | bConfigurationValue
+-----------+--------+------------+---------------------------------------------------
+ 0x13D | 1 | | bNumConfigurations
+-----------+--------+------------+---------------------------------------------------
+ 0x13E | 1 | | bNumInterfaces
+
+USBIP_CMD_SUBMIT: Submit an URB
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000001 | command: Submit an URB
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: the sequence number of the URB to submit
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number, possible values are: 0...15
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | transfer_flags: possible values depend on the
+ | | | URB transfer type, see below
+-----------+--------+------------+---------------------------------------------------
+ 0x18 | 4 | | transfer_buffer_length
+-----------+--------+------------+---------------------------------------------------
+ 0x1C | 4 | | start_frame: specify the selected frame to
+ | | | transmit an ISO frame, ignored if URB_ISO_ASAP
+ | | | is specified at transfer_flags
+-----------+--------+------------+---------------------------------------------------
+ 0x20 | 4 | | number_of_packets: number of ISO packets
+-----------+--------+------------+---------------------------------------------------
+ 0x24 | 4 | | interval: maximum time for the request on the
+ | | | server-side host controller
+-----------+--------+------------+---------------------------------------------------
+ 0x28 | 8 | | setup: data bytes for USB setup, filled with
+ | | | zeros if not used
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | | | URB data. For ISO transfers the padding between
+ | | | each ISO packets is not transmitted.
+
+
+ Allowed transfer_flags | value | control | interrupt | bulk | isochronous
+ -------------------------+------------+---------+-----------+----------+-------------
+ URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no
+ URB_ISO_ASAP | 0x00000002 | no | no | no | yes
+ URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes
+ URB_NO_FSBR | 0x00000020 | yes | no | no | no
+ URB_ZERO_PACKET | 0x00000040 | no | no | only out | no
+ URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes
+ URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes
+ URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes
+
+
+USBIP_RET_SUBMIT: Reply for submitting an URB
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000003 | command
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: URB sequence number
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | status: zero for successful URB transaction,
+ | | | otherwise some kind of error happened.
+-----------+--------+------------+---------------------------------------------------
+ 0x18 | 4 | n | actual_length: number of URB data bytes
+-----------+--------+------------+---------------------------------------------------
+ 0x1C | 4 | | start_frame: for an ISO frame the actually
+ | | | selected frame for transmit.
+-----------+--------+------------+---------------------------------------------------
+ 0x20 | 4 | | number_of_packets
+-----------+--------+------------+---------------------------------------------------
+ 0x24 | 4 | | error_count
+-----------+--------+------------+---------------------------------------------------
+ 0x28 | 8 | | setup: data bytes for USB setup, filled with
+ | | | zeros if not used
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | n | | URB data bytes. For ISO transfers the padding
+ | | | between each ISO packets is not transmitted.
+
+USBIP_CMD_UNLINK: Unlink an URB
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000002 | command: URB unlink command
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so?
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number: zero
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | seqnum: the URB sequence number given previously
+ | | | at USBIP_CMD_SUBMIT.seqnum field
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | n | | URB data bytes. For ISO transfers the padding
+ | | | between each ISO packets is not transmitted.
+
+USBIP_RET_UNLINK: Reply for URB unlink
+
+ Offset | Length | Value | Description
+-----------+--------+------------+---------------------------------------------------
+ 0 | 4 | 0x00000004 | command: reply for the URB unlink command
+-----------+--------+------------+---------------------------------------------------
+ 4 | 4 | | seqnum: the unlinked URB sequence number
+-----------+--------+------------+---------------------------------------------------
+ 8 | 4 | | devid
+-----------+--------+------------+---------------------------------------------------
+ 0xC | 4 | | direction: 0: USBIP_DIR_OUT
+ | | | 1: USBIP_DIR_IN
+-----------+--------+------------+---------------------------------------------------
+ 0x10 | 4 | | ep: endpoint number
+-----------+--------+------------+---------------------------------------------------
+ 0x14 | 4 | | status: This is the value contained in the
+ | | | urb->status in the URB completition handler.
+ | | | FIXME: a better explanation needed.
+-----------+--------+------------+---------------------------------------------------
+ 0x30 | n | | URB data bytes. For ISO transfers the padding
+ | | | between each ISO packets is not transmitted.
diff --git a/drivers/staging/usbip/userspace/.gitignore b/drivers/staging/usbip/userspace/.gitignore
new file mode 100644
index 00000000000..9aad9e30a8b
--- /dev/null
+++ b/drivers/staging/usbip/userspace/.gitignore
@@ -0,0 +1,28 @@
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libsrc/Makefile
+libsrc/Makefile.in
+libtool
+ltmain.sh
+missing
+src/Makefile
+src/Makefile.in
+stamp-h1
+libsrc/libusbip.la
+libsrc/libusbip_la-names.lo
+libsrc/libusbip_la-usbip_common.lo
+libsrc/libusbip_la-usbip_host_driver.lo
+libsrc/libusbip_la-vhci_driver.lo
+src/usbip
+src/usbipd
diff --git a/drivers/staging/usbip/userspace/AUTHORS b/drivers/staging/usbip/userspace/AUTHORS
new file mode 100644
index 00000000000..a27ea8d03ae
--- /dev/null
+++ b/drivers/staging/usbip/userspace/AUTHORS
@@ -0,0 +1,3 @@
+Takahiro Hirofuchi
+Robert Leibl
+matt mooney <mfm@muteddisk.com>
diff --git a/drivers/staging/usbip/userspace/COPYING b/drivers/staging/usbip/userspace/COPYING
new file mode 100644
index 00000000000..c5611e48a8e
--- /dev/null
+++ b/drivers/staging/usbip/userspace/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/drivers/staging/usbip/userspace/INSTALL b/drivers/staging/usbip/userspace/INSTALL
new file mode 100644
index 00000000000..d3c5b40a940
--- /dev/null
+++ b/drivers/staging/usbip/userspace/INSTALL
@@ -0,0 +1,237 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 6. Often, you can also type `make uninstall' to remove the installed
+ files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/drivers/staging/usbip/userspace/Makefile.am b/drivers/staging/usbip/userspace/Makefile.am
new file mode 100644
index 00000000000..66f8bf038c9
--- /dev/null
+++ b/drivers/staging/usbip/userspace/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS := libsrc src
+includedir = @includedir@/usbip
+include_HEADERS := $(addprefix libsrc/, \
+ usbip_common.h vhci_driver.h usbip_host_driver.h)
+
+dist_man_MANS := $(addprefix doc/, usbip.8 usbipd.8)
diff --git a/drivers/staging/usbip/userspace/README b/drivers/staging/usbip/userspace/README
new file mode 100644
index 00000000000..831f49fea3c
--- /dev/null
+++ b/drivers/staging/usbip/userspace/README
@@ -0,0 +1,202 @@
+#
+# README for usbip-utils
+#
+# Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+# 2005-2008 Takahiro Hirofuchi
+
+
+[Requirements]
+ - USB/IP device drivers
+ Found in the staging directory of the Linux kernel.
+
+ - libudev >= 2.0
+ libudev library
+
+ - libwrap0-dev
+ tcp wrapper library
+
+ - gcc >= 4.0
+
+ - libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config
+
+[Optional]
+ - hwdata
+ Contains USB device identification data.
+
+
+[Install]
+ 0. Generate configuration scripts.
+ $ ./autogen.sh
+
+ 1. Compile & install the userspace utilities.
+ $ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>]
+ $ make install
+
+ 2. Compile & install USB/IP drivers.
+
+
+[Usage]
+ server:# (Physically attach your USB device.)
+
+ server:# insmod usbip-core.ko
+ server:# insmod usbip-host.ko
+
+ server:# usbipd -D
+ - Start usbip daemon.
+
+ server:# usbip list -l
+ - List driver assignments for USB devices.
+
+ server:# usbip bind --busid 1-2
+ - Bind usbip-host.ko to the device with busid 1-2.
+ - The USB device 1-2 is now exportable to other hosts!
+ - Use `usbip unbind --busid 1-2' to stop exporting the device.
+
+ client:# insmod usbip-core.ko
+ client:# insmod vhci-hcd.ko
+
+ client:# usbip list --remote <host>
+ - List exported USB devices on the <host>.
+
+ client:# usbip attach --remote <host> --busid 1-2
+ - Connect the remote USB device.
+
+ client:# usbip port
+ - Show virtual port status.
+
+ client:# usbip detach --port <port>
+ - Detach the USB device.
+
+
+[Example]
+---------------------------
+ SERVER SIDE
+---------------------------
+Physically attach your USB devices to this host.
+
+ trois:# insmod path/to/usbip-core.ko
+ trois:# insmod path/to/usbip-host.ko
+ trois:# usbipd -D
+
+In another terminal, let's look up what USB devices are physically
+attached to this host.
+
+ trois:# usbip list -l
+ Local USB devices
+ =================
+ - busid 1-1 (05a9:a511)
+ 1-1:1.0 -> ov511
+
+ - busid 3-2 (0711:0902)
+ 3-2:1.0 -> none
+
+ - busid 3-3.1 (08bb:2702)
+ 3-3.1:1.0 -> snd-usb-audio
+ 3-3.1:1.1 -> snd-usb-audio
+
+ - busid 3-3.2 (04bb:0206)
+ 3-3.2:1.0 -> usb-storage
+
+ - busid 3-3 (0409:0058)
+ 3-3:1.0 -> hub
+
+ - busid 4-1 (046d:08b2)
+ 4-1:1.0 -> none
+ 4-1:1.1 -> none
+ 4-1:1.2 -> none
+
+ - busid 5-2 (058f:9254)
+ 5-2:1.0 -> hub
+
+A USB storage device of busid 3-3.2 is now bound to the usb-storage
+driver. To export this device, we first mark the device as
+"exportable"; the device is bound to the usbip-host driver. Please
+remember you can not export a USB hub.
+
+Mark the device of busid 3-3.2 as exportable:
+
+ trois:# usbip --debug bind --busid 3-3.2
+ ...
+ usbip debug: usbip_bind.c:162:[unbind_other] 3-3.2:1.0 -> usb-storage
+ ...
+ bind device on busid 3-3.2: complete
+
+ trois:# usbip list -l
+ Local USB devices
+ =================
+ ...
+
+ - busid 3-3.2 (04bb:0206)
+ 3-3.2:1.0 -> usbip-host
+ ...
+
+---------------------------
+ CLIENT SIDE
+---------------------------
+First, let's list available remote devices that are marked as
+exportable on the host.
+
+ deux:# insmod path/to/usbip-core.ko
+ deux:# insmod path/to/vhci-hcd.ko
+
+ deux:# usbip list --remote 10.0.0.3
+ Exportable USB devices
+ ======================
+ - 10.0.0.3
+ 1-1: Prolific Technology, Inc. : unknown product (067b:3507)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Mass Storage / SCSI / Bulk (Zip) (08/06/50)
+
+ 1-2.2.1: Apple Computer, Inc. : unknown product (05ac:0203)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Human Interface Devices / Boot Interface Subclass / Keyboard (03/01/01)
+
+ 1-2.2.3: OmniVision Technologies, Inc. : OV511+ WebCam (05a9:a511)
+ : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.3
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/00)
+
+ 3-1: Logitech, Inc. : QuickCam Pro 4000 (046d:08b2)
+ : /sys/devices/pci0000:00/0000:00:1e.0/0000:02:0a.0/usb3/3-1
+ : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
+ : 0 - Data / unknown subclass / unknown protocol (0a/ff/00)
+ : 1 - Audio / Control Device / unknown protocol (01/01/00)
+ : 2 - Audio / Streaming / unknown protocol (01/02/00)
+
+Attach a remote USB device:
+
+ deux:# usbip attach --remote 10.0.0.3 --busid 1-1
+ port 0 attached
+
+Show the devices attached to this client:
+
+ deux:# usbip port
+ Port 00: <Port in Use> at Full Speed(12Mbps)
+ Prolific Technology, Inc. : unknown product (067b:3507)
+ 6-1 -> usbip://10.0.0.3:3240/1-1 (remote bus/dev 001/004)
+ 6-1:1.0 used by usb-storage
+ /sys/class/scsi_device/0:0:0:0/device
+ /sys/class/scsi_host/host0/device
+ /sys/block/sda/device
+
+Detach the imported device:
+
+ deux:# usbip detach --port 0
+ port 0 detached
+
+
+[Checklist]
+ - See 'Debug Tips' on the project wiki.
+ - http://usbip.wiki.sourceforge.net/how-to-debug-usbip
+ - usbip-host.ko must be bound to the target device.
+ - See /proc/bus/usb/devices and find "Driver=..." lines of the device.
+ - Shutdown firewall.
+ - usbip now uses TCP port 3240.
+ - Disable SELinux.
+ - Check the kernel and daemon messages.
+
+
+[Contact]
+ Mailing List: linux-usb@vger.kernel.org
diff --git a/drivers/staging/usbip/userspace/autogen.sh b/drivers/staging/usbip/userspace/autogen.sh
new file mode 100755
index 00000000000..e1112d3fcbf
--- /dev/null
+++ b/drivers/staging/usbip/userspace/autogen.sh
@@ -0,0 +1,9 @@
+#!/bin/sh -x
+
+#aclocal
+#autoheader
+#libtoolize --copy --force
+#automake-1.9 -acf
+#autoconf
+
+autoreconf -i -f -v
diff --git a/drivers/staging/usbip/userspace/cleanup.sh b/drivers/staging/usbip/userspace/cleanup.sh
new file mode 100755
index 00000000000..955c3ccb729
--- /dev/null
+++ b/drivers/staging/usbip/userspace/cleanup.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if [ -r Makefile ]; then
+ make distclean
+fi
+
+FILES="aclocal.m4 autom4te.cache compile config.guess config.h.in config.log \
+ config.status config.sub configure cscope.out depcomp install-sh \
+ libsrc/Makefile libsrc/Makefile.in libtool ltmain.sh Makefile \
+ Makefile.in missing src/Makefile src/Makefile.in"
+
+rm -vRf $FILES
diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
new file mode 100644
index 00000000000..607d05c5ccf
--- /dev/null
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -0,0 +1,111 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+AC_INIT([usbip-utils], [2.0], [linux-usb@vger.kernel.org])
+AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
+
+CURRENT=0
+REVISION=1
+AGE=0
+AC_SUBST([LIBUSBIP_VERSION], [$CURRENT:$REVISION:$AGE], [library version])
+
+AC_CONFIG_SRCDIR([src/usbipd.c])
+AC_CONFIG_HEADERS([config.h])
+
+AM_INIT_AUTOMAKE([foreign])
+LT_INIT
+
+# Silent build for automake >= 1.11
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_SUBST([EXTRA_CFLAGS], ["-Wall -Werror -Wextra -std=gnu99"])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h dnl
+ string.h sys/socket.h syslog.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_INT32_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([memset mkdir regcomp socket strchr strerror strstr dnl
+ strtoul])
+
+AC_CHECK_HEADER([libudev.h],
+ [AC_CHECK_LIB([udev], [udev_new],
+ [LIBS="$LIBS -ludev"],
+ [AC_MSG_ERROR([Missing udev library!])])],
+ [AC_MSG_ERROR([Missing /usr/include/libudev.h])])
+
+# Checks for libwrap library.
+AC_MSG_CHECKING([whether to use the libwrap (TCP wrappers) library])
+AC_ARG_WITH([tcp-wrappers],
+ [AS_HELP_STRING([--with-tcp-wrappers],
+ [use the libwrap (TCP wrappers) library])],
+ dnl [ACTION-IF-GIVEN]
+ [if test "$withval" = "yes"; then
+ AC_MSG_RESULT([yes])
+ AC_MSG_CHECKING([for hosts_access in -lwrap])
+ saved_LIBS="$LIBS"
+ LIBS="-lwrap $saved_LIBS"
+ AC_TRY_LINK(
+ [int hosts_access(); int allow_severity, deny_severity;],
+ [hosts_access()],
+ [AC_MSG_RESULT([yes]);
+ AC_DEFINE([HAVE_LIBWRAP], [1],
+ [use tcp wrapper]) wrap_LIB="-lwrap"],
+ [AC_MSG_RESULT([not found]); exit 1])
+ else
+ AC_MSG_RESULT([no]);
+ fi],
+ dnl [ACTION-IF-NOT-GIVEN]
+ [AC_MSG_RESULT([(default)])
+ AC_MSG_CHECKING([for hosts_access in -lwrap])
+ saved_LIBS="$LIBS"
+ LIBS="-lwrap $saved_LIBS"
+ AC_TRY_LINK(
+ [int hosts_access(); int allow_severity, deny_severity;],
+ [hosts_access()],
+ [AC_MSG_RESULT([yes]);
+ AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
+ [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
+
+# Sets directory containing usb.ids.
+AC_ARG_WITH([usbids-dir],
+ [AS_HELP_STRING([--with-usbids-dir=DIR],
+ [where usb.ids is found (default /usr/share/hwdata/)])],
+ [USBIDS_DIR=$withval], [USBIDS_DIR="/usr/share/hwdata/"])
+AC_SUBST([USBIDS_DIR])
+
+# use _FORTIFY_SOURCE
+AC_MSG_CHECKING([whether to use fortify])
+AC_ARG_WITH([fortify],
+ [AS_HELP_STRING([--with-fortify],
+ [use _FORTIFY_SROUCE option when compiling)])],
+ dnl [ACTION-IF-GIVEN]
+ [if test "$withval" = "yes"; then
+ AC_MSG_RESULT([yes])
+ CFLAGS="$CFLAGS -D_FORTIFY_SOURCE -O"
+ else
+ AC_MSG_RESULT([no])
+ CFLAGS="$CFLAGS -U_FORTIFY_SOURCE"
+ fi
+ ],
+ dnl [ACTION-IF-NOT-GIVEN]
+ [AC_MSG_RESULT([default])])
+
+AC_CONFIG_FILES([Makefile libsrc/Makefile src/Makefile])
+AC_OUTPUT
diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
new file mode 100644
index 00000000000..a6097be25d2
--- /dev/null
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -0,0 +1,95 @@
+.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
+.SH NAME
+usbip \- manage USB/IP devices
+.SH SYNOPSIS
+.B usbip
+[\fIoptions\fR] <\fIcommand\fR> <\fIargs\fR>
+
+.SH DESCRIPTION
+On a USB/IP server, devices can be listed, bound, and unbound using
+this program. On a USB/IP client, devices exported by USB/IP servers
+can be listed, attached and detached.
+
+.SH OPTIONS
+.HP
+\fB\-\-debug\fR
+.IP
+Print debugging information.
+.PP
+
+.HP
+\fB\-\-log\fR
+.IP
+Log to syslog.
+.PP
+
+.HP
+\fB\-\-tcp-port PORT\fR
+.IP
+Connect to PORT on remote host (used for attach and list --remote).
+.PP
+
+.SH COMMANDS
+.HP
+\fBversion\fR
+.IP
+Show version and exit.
+.PP
+
+.HP
+\fBhelp\fR [\fIcommand\fR]
+.IP
+Print the program help message, or help on a specific command, and
+then exit.
+.PP
+
+.HP
+\fBattach\fR \-\-remote=<\fIhost\fR> \-\-busid=<\fIbus_id\fR>
+.IP
+Attach a remote USB device.
+.PP
+
+.HP
+\fBdetach\fR \-\-port=<\fIport\fR>
+.IP
+Detach an imported USB device.
+.PP
+
+.HP
+\fBbind\fR \-\-busid=<\fIbusid\fR>
+.IP
+Make a device exportable.
+.PP
+
+.HP
+\fBunbind\fR \-\-busid=<\fIbusid\fR>
+.IP
+Stop exporting a device so it can be used by a local driver.
+.PP
+
+.HP
+\fBlist\fR \-\-remote=<\fIhost\fR>
+.IP
+List USB devices exported by a remote host.
+.PP
+
+.HP
+\fBlist\fR \-\-local
+.IP
+List local USB devices.
+.PP
+
+
+.SH EXAMPLES
+
+ client:# usbip list --remote=server
+ - List exportable usb devices on the server.
+
+ client:# usbip attach --remote=server --busid=1-2
+ - Connect the remote USB device.
+
+ client:# usbip detach --port=0
+ - Detach the usb device.
+
+.SH "SEE ALSO"
+\fBusbipd\fP\fB(8)\fB\fP
diff --git a/drivers/staging/usbip/userspace/doc/usbipd.8 b/drivers/staging/usbip/userspace/doc/usbipd.8
new file mode 100644
index 00000000000..ac4635db3f0
--- /dev/null
+++ b/drivers/staging/usbip/userspace/doc/usbipd.8
@@ -0,0 +1,91 @@
+.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
+.SH NAME
+usbipd \- USB/IP server daemon
+.SH SYNOPSIS
+.B usbipd
+[\fIoptions\fR]
+
+.SH DESCRIPTION
+.B usbipd
+provides USB/IP clients access to exported USB devices.
+
+Devices have to explicitly be exported using
+.B usbip bind
+before usbipd makes them available to other hosts.
+
+The daemon accepts connections from USB/IP clients
+on TCP port 3240 by default.
+
+.SH OPTIONS
+.HP
+\fB\-4\fR, \fB\-\-ipv4\fR
+.IP
+Bind to IPv4. Default is both.
+.PP
+
+.HP
+\fB\-6\fR, \fB\-\-ipv6\fR
+.IP
+Bind to IPv6. Default is both.
+.PP
+
+.HP
+\fB\-D\fR, \fB\-\-daemon\fR
+.IP
+Run as a daemon process.
+.PP
+
+.HP
+\fB\-d\fR, \fB\-\-debug\fR
+.IP
+Print debugging information.
+.PP
+
+.HP
+\fB\-PFILE\fR, \fB\-\-pid FILE\fR
+.IP
+Write process id to FILE.
+.br
+If no FILE specified, use /var/run/usbipd.pid
+.PP
+
+\fB\-tPORT\fR, \fB\-\-tcp\-port PORT\fR
+.IP
+Listen on TCP/IP port PORT.
+.PP
+
+\fB\-h\fR, \fB\-\-help\fR
+.IP
+Print the program help message and exit.
+.PP
+
+.HP
+\fB\-v\fR, \fB\-\-version\fR
+.IP
+Show version.
+.PP
+
+.SH LIMITATIONS
+
+.B usbipd
+offers no authentication or authorization for USB/IP. Any
+USB/IP client can connect and use exported devices.
+
+.SH EXAMPLES
+
+ server:# modprobe usbip
+
+ server:# usbipd -D
+ - Start usbip daemon.
+
+ server:# usbip list --local
+ - List driver assignments for usb devices.
+
+ server:# usbip bind --busid=1-2
+ - Bind usbip-host.ko to the device of busid 1-2.
+ - A usb device 1-2 is now exportable to other hosts!
+ - Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally.
+
+.SH "SEE ALSO"
+\fBusbip\fP\fB(8)\fB\fP
+
diff --git a/drivers/staging/usbip/userspace/libsrc/Makefile.am b/drivers/staging/usbip/userspace/libsrc/Makefile.am
new file mode 100644
index 00000000000..7c8f8a4d54e
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/Makefile.am
@@ -0,0 +1,8 @@
+libusbip_la_CPPFLAGS = -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
+libusbip_la_CFLAGS = @EXTRA_CFLAGS@
+libusbip_la_LDFLAGS = -version-info @LIBUSBIP_VERSION@
+
+lib_LTLIBRARIES := libusbip.la
+libusbip_la_SOURCES := names.c names.h usbip_host_driver.c usbip_host_driver.h \
+ usbip_common.c usbip_common.h vhci_driver.c vhci_driver.h \
+ sysfs_utils.c sysfs_utils.h
diff --git a/drivers/staging/usbip/userspace/libsrc/list.h b/drivers/staging/usbip/userspace/libsrc/list.h
new file mode 100644
index 00000000000..8d0c936e184
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/list.h
@@ -0,0 +1,136 @@
+#ifndef _LIST_H
+#define _LIST_H
+
+/* Stripped down implementation of linked list taken
+ * from the Linux Kernel.
+ */
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+#define POISON_POINTER_DELTA 0
+#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
+#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#endif
diff --git a/drivers/staging/usbip/userspace/libsrc/names.c b/drivers/staging/usbip/userspace/libsrc/names.c
new file mode 100644
index 00000000000..81ff8522405
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/names.c
@@ -0,0 +1,504 @@
+/*
+ * names.c -- USB name database manipulation routines
+ *
+ * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ *
+ *
+ * Copyright (C) 2005 Takahiro Hirofuchi
+ * - names_deinit() is added.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "names.h"
+#include "usbip_common.h"
+
+struct vendor {
+ struct vendor *next;
+ u_int16_t vendorid;
+ char name[1];
+};
+
+struct product {
+ struct product *next;
+ u_int16_t vendorid, productid;
+ char name[1];
+};
+
+struct class {
+ struct class *next;
+ u_int8_t classid;
+ char name[1];
+};
+
+struct subclass {
+ struct subclass *next;
+ u_int8_t classid, subclassid;
+ char name[1];
+};
+
+struct protocol {
+ struct protocol *next;
+ u_int8_t classid, subclassid, protocolid;
+ char name[1];
+};
+
+struct genericstrtable {
+ struct genericstrtable *next;
+ unsigned int num;
+ char name[1];
+};
+
+
+#define HASH1 0x10
+#define HASH2 0x02
+#define HASHSZ 16
+
+static unsigned int hashnum(unsigned int num)
+{
+ unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27;
+
+ for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1)
+ if (num & mask1)
+ num ^= mask2;
+ return num & (HASHSZ-1);
+}
+
+
+static struct vendor *vendors[HASHSZ] = { NULL, };
+static struct product *products[HASHSZ] = { NULL, };
+static struct class *classes[HASHSZ] = { NULL, };
+static struct subclass *subclasses[HASHSZ] = { NULL, };
+static struct protocol *protocols[HASHSZ] = { NULL, };
+
+const char *names_vendor(u_int16_t vendorid)
+{
+ struct vendor *v;
+
+ v = vendors[hashnum(vendorid)];
+ for (; v; v = v->next)
+ if (v->vendorid == vendorid)
+ return v->name;
+ return NULL;
+}
+
+const char *names_product(u_int16_t vendorid, u_int16_t productid)
+{
+ struct product *p;
+
+ p = products[hashnum((vendorid << 16) | productid)];
+ for (; p; p = p->next)
+ if (p->vendorid == vendorid && p->productid == productid)
+ return p->name;
+ return NULL;
+}
+
+const char *names_class(u_int8_t classid)
+{
+ struct class *c;
+
+ c = classes[hashnum(classid)];
+ for (; c; c = c->next)
+ if (c->classid == classid)
+ return c->name;
+ return NULL;
+}
+
+const char *names_subclass(u_int8_t classid, u_int8_t subclassid)
+{
+ struct subclass *s;
+
+ s = subclasses[hashnum((classid << 8) | subclassid)];
+ for (; s; s = s->next)
+ if (s->classid == classid && s->subclassid == subclassid)
+ return s->name;
+ return NULL;
+}
+
+const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid)
+{
+ struct protocol *p;
+
+ p = protocols[hashnum((classid << 16) | (subclassid << 8)
+ | protocolid)];
+ for (; p; p = p->next)
+ if (p->classid == classid && p->subclassid == subclassid &&
+ p->protocolid == protocolid)
+ return p->name;
+ return NULL;
+}
+
+/* add a cleanup function by takahiro */
+struct pool {
+ struct pool *next;
+ void *mem;
+};
+
+static struct pool *pool_head;
+
+static void *my_malloc(size_t size)
+{
+ struct pool *p;
+
+ p = calloc(1, sizeof(struct pool));
+ if (!p)
+ return NULL;
+
+ p->mem = calloc(1, size);
+ if (!p->mem) {
+ free(p);
+ return NULL;
+ }
+
+ p->next = pool_head;
+ pool_head = p;
+
+ return p->mem;
+}
+
+void names_free(void)
+{
+ struct pool *pool;
+
+ if (!pool_head)
+ return;
+
+ for (pool = pool_head; pool != NULL; ) {
+ struct pool *tmp;
+
+ if (pool->mem)
+ free(pool->mem);
+
+ tmp = pool;
+ pool = pool->next;
+ free(tmp);
+ }
+}
+
+static int new_vendor(const char *name, u_int16_t vendorid)
+{
+ struct vendor *v;
+ unsigned int h = hashnum(vendorid);
+
+ v = vendors[h];
+ for (; v; v = v->next)
+ if (v->vendorid == vendorid)
+ return -1;
+ v = my_malloc(sizeof(struct vendor) + strlen(name));
+ if (!v)
+ return -1;
+ strcpy(v->name, name);
+ v->vendorid = vendorid;
+ v->next = vendors[h];
+ vendors[h] = v;
+ return 0;
+}
+
+static int new_product(const char *name, u_int16_t vendorid,
+ u_int16_t productid)
+{
+ struct product *p;
+ unsigned int h = hashnum((vendorid << 16) | productid);
+
+ p = products[h];
+ for (; p; p = p->next)
+ if (p->vendorid == vendorid && p->productid == productid)
+ return -1;
+ p = my_malloc(sizeof(struct product) + strlen(name));
+ if (!p)
+ return -1;
+ strcpy(p->name, name);
+ p->vendorid = vendorid;
+ p->productid = productid;
+ p->next = products[h];
+ products[h] = p;
+ return 0;
+}
+
+static int new_class(const char *name, u_int8_t classid)
+{
+ struct class *c;
+ unsigned int h = hashnum(classid);
+
+ c = classes[h];
+ for (; c; c = c->next)
+ if (c->classid == classid)
+ return -1;
+ c = my_malloc(sizeof(struct class) + strlen(name));
+ if (!c)
+ return -1;
+ strcpy(c->name, name);
+ c->classid = classid;
+ c->next = classes[h];
+ classes[h] = c;
+ return 0;
+}
+
+static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid)
+{
+ struct subclass *s;
+ unsigned int h = hashnum((classid << 8) | subclassid);
+
+ s = subclasses[h];
+ for (; s; s = s->next)
+ if (s->classid == classid && s->subclassid == subclassid)
+ return -1;
+ s = my_malloc(sizeof(struct subclass) + strlen(name));
+ if (!s)
+ return -1;
+ strcpy(s->name, name);
+ s->classid = classid;
+ s->subclassid = subclassid;
+ s->next = subclasses[h];
+ subclasses[h] = s;
+ return 0;
+}
+
+static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid)
+{
+ struct protocol *p;
+ unsigned int h = hashnum((classid << 16) | (subclassid << 8)
+ | protocolid);
+
+ p = protocols[h];
+ for (; p; p = p->next)
+ if (p->classid == classid && p->subclassid == subclassid
+ && p->protocolid == protocolid)
+ return -1;
+ p = my_malloc(sizeof(struct protocol) + strlen(name));
+ if (!p)
+ return -1;
+ strcpy(p->name, name);
+ p->classid = classid;
+ p->subclassid = subclassid;
+ p->protocolid = protocolid;
+ p->next = protocols[h];
+ protocols[h] = p;
+ return 0;
+}
+
+static void parse(FILE *f)
+{
+ char buf[512], *cp;
+ unsigned int linectr = 0;
+ int lastvendor = -1;
+ int lastclass = -1;
+ int lastsubclass = -1;
+ int lasthut = -1;
+ int lastlang = -1;
+ unsigned int u;
+
+ while (fgets(buf, sizeof(buf), f)) {
+ linectr++;
+ /* remove line ends */
+ cp = strchr(buf, '\r');
+ if (cp)
+ *cp = 0;
+ cp = strchr(buf, '\n');
+ if (cp)
+ *cp = 0;
+ if (buf[0] == '#' || !buf[0])
+ continue;
+ cp = buf;
+ if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' &&
+ buf[3] == 'S' && buf[4] == 'D' &&
+ buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/
+ buf[7] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'P' && buf[1] == 'H' &&
+ buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' &&
+ buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') {
+ lasthut = lastclass = lastvendor = lastsubclass = -1;
+ /*
+ * set 1 as pseudo-id to indicate that the parser is
+ * in a `L' section.
+ */
+ lastlang = 1;
+ continue;
+ }
+ if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') {
+ /* class spec */
+ cp = buf+2;
+ while (isspace(*cp))
+ cp++;
+ if (!isxdigit(*cp)) {
+ err("Invalid class spec at line %u", linectr);
+ continue;
+ }
+ u = strtoul(cp, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid class spec at line %u", linectr);
+ continue;
+ }
+ if (new_class(cp, u))
+ err("Duplicate class spec at line %u class %04x %s",
+ linectr, u, cp);
+ dbg("line %5u class %02x %s", linectr, u, cp);
+ lasthut = lastlang = lastvendor = lastsubclass = -1;
+ lastclass = u;
+ continue;
+ }
+ if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) {
+ /* audio terminal type spec */
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C'
+ && isspace(buf[3])) {
+ /* HID Descriptor bCountryCode */
+ continue;
+ }
+ if (isxdigit(*cp)) {
+ /* vendor */
+ u = strtoul(cp, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid vendor spec at line %u", linectr);
+ continue;
+ }
+ if (new_vendor(cp, u))
+ err("Duplicate vendor spec at line %u vendor %04x %s",
+ linectr, u, cp);
+ dbg("line %5u vendor %04x %s", linectr, u, cp);
+ lastvendor = u;
+ lasthut = lastlang = lastclass = lastsubclass = -1;
+ continue;
+ }
+ if (buf[0] == '\t' && isxdigit(buf[1])) {
+ /* product or subclass spec */
+ u = strtoul(buf+1, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid product/subclass spec at line %u",
+ linectr);
+ continue;
+ }
+ if (lastvendor != -1) {
+ if (new_product(cp, lastvendor, u))
+ err("Duplicate product spec at line %u product %04x:%04x %s",
+ linectr, lastvendor, u, cp);
+ dbg("line %5u product %04x:%04x %s", linectr,
+ lastvendor, u, cp);
+ continue;
+ }
+ if (lastclass != -1) {
+ if (new_subclass(cp, lastclass, u))
+ err("Duplicate subclass spec at line %u class %02x:%02x %s",
+ linectr, lastclass, u, cp);
+ dbg("line %5u subclass %02x:%02x %s", linectr,
+ lastclass, u, cp);
+ lastsubclass = u;
+ continue;
+ }
+ if (lasthut != -1) {
+ /* do not store hut */
+ continue;
+ }
+ if (lastlang != -1) {
+ /* do not store langid */
+ continue;
+ }
+ err("Product/Subclass spec without prior Vendor/Class spec at line %u",
+ linectr);
+ continue;
+ }
+ if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) {
+ /* protocol spec */
+ u = strtoul(buf+2, &cp, 16);
+ while (isspace(*cp))
+ cp++;
+ if (!*cp) {
+ err("Invalid protocol spec at line %u",
+ linectr);
+ continue;
+ }
+ if (lastclass != -1 && lastsubclass != -1) {
+ if (new_protocol(cp, lastclass, lastsubclass,
+ u))
+ err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s",
+ linectr, lastclass, lastsubclass,
+ u, cp);
+ dbg("line %5u protocol %02x:%02x:%02x %s",
+ linectr, lastclass, lastsubclass, u, cp);
+ continue;
+ }
+ err("Protocol spec without prior Class and Subclass spec at line %u",
+ linectr);
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'I' &&
+ buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ continue;
+ }
+ if (buf[0] == 'H' && buf[1] == 'U' &&
+ buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') {
+ lastlang = lastclass = lastvendor = lastsubclass = -1;
+ /*
+ * set 1 as pseudo-id to indicate that the parser is
+ * in a `HUT' section.
+ */
+ lasthut = 1;
+ continue;
+ }
+ if (buf[0] == 'R' && buf[1] == ' ')
+ continue;
+
+ if (buf[0] == 'V' && buf[1] == 'T')
+ continue;
+
+ err("Unknown line at line %u", linectr);
+ }
+}
+
+
+int names_init(char *n)
+{
+ FILE *f;
+
+ f = fopen(n, "r");
+ if (!f)
+ return errno;
+
+ parse(f);
+ fclose(f);
+ return 0;
+}
diff --git a/drivers/staging/usbip/userspace/libsrc/names.h b/drivers/staging/usbip/userspace/libsrc/names.h
new file mode 100644
index 00000000000..680926512de
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/names.h
@@ -0,0 +1,41 @@
+/*
+ * names.h -- USB name database manipulation routines
+ *
+ * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ * Copyright (C) 2005 Takahiro Hirofuchi
+ * - names_free() is added.
+ */
+
+#ifndef _NAMES_H
+#define _NAMES_H
+
+#include <sys/types.h>
+
+/* used by usbip_common.c */
+extern const char *names_vendor(u_int16_t vendorid);
+extern const char *names_product(u_int16_t vendorid, u_int16_t productid);
+extern const char *names_class(u_int8_t classid);
+extern const char *names_subclass(u_int8_t classid, u_int8_t subclassid);
+extern const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
+ u_int8_t protocolid);
+extern int names_init(char *n);
+extern void names_free(void);
+
+#endif /* _NAMES_H */
diff --git a/drivers/staging/usbip/userspace/libsrc/sysfs_utils.c b/drivers/staging/usbip/userspace/libsrc/sysfs_utils.c
new file mode 100644
index 00000000000..36ac88ece0b
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/sysfs_utils.c
@@ -0,0 +1,31 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "sysfs_utils.h"
+#include "usbip_common.h"
+
+int write_sysfs_attribute(const char *attr_path, const char *new_value,
+ size_t len)
+{
+ int fd;
+ int length;
+
+ fd = open(attr_path, O_WRONLY);
+ if (fd < 0) {
+ dbg("error opening attribute %s", attr_path);
+ return -1;
+ }
+
+ length = write(fd, new_value, len);
+ if (length < 0) {
+ dbg("error writing to attribute %s", attr_path);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
diff --git a/drivers/staging/usbip/userspace/libsrc/sysfs_utils.h b/drivers/staging/usbip/userspace/libsrc/sysfs_utils.h
new file mode 100644
index 00000000000..32ac1d105d1
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/sysfs_utils.h
@@ -0,0 +1,8 @@
+
+#ifndef __SYSFS_UTILS_H
+#define __SYSFS_UTILS_H
+
+int write_sysfs_attribute(const char *attr_path, const char *new_value,
+ size_t len);
+
+#endif
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.c b/drivers/staging/usbip/userspace/libsrc/usbip_common.c
new file mode 100644
index 00000000000..ac73710473d
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#include <libudev.h>
+#include "usbip_common.h"
+#include "names.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+int usbip_use_syslog;
+int usbip_use_stderr;
+int usbip_use_debug;
+
+extern struct udev *udev_context;
+
+struct speed_string {
+ int num;
+ char *speed;
+ char *desc;
+};
+
+static const struct speed_string speed_strings[] = {
+ { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"},
+ { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" },
+ { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" },
+ { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
+ { USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
+ { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
+ { 0, NULL, NULL }
+};
+
+struct portst_string {
+ int num;
+ char *desc;
+};
+
+static struct portst_string portst_strings[] = {
+ { SDEV_ST_AVAILABLE, "Device Available" },
+ { SDEV_ST_USED, "Device in Use" },
+ { SDEV_ST_ERROR, "Device Error"},
+ { VDEV_ST_NULL, "Port Available"},
+ { VDEV_ST_NOTASSIGNED, "Port Initializing"},
+ { VDEV_ST_USED, "Port in Use"},
+ { VDEV_ST_ERROR, "Port Error"},
+ { 0, NULL}
+};
+
+const char *usbip_status_string(int32_t status)
+{
+ for (int i = 0; portst_strings[i].desc != NULL; i++)
+ if (portst_strings[i].num == status)
+ return portst_strings[i].desc;
+
+ return "Unknown Status";
+}
+
+const char *usbip_speed_string(int num)
+{
+ for (int i = 0; speed_strings[i].speed != NULL; i++)
+ if (speed_strings[i].num == num)
+ return speed_strings[i].desc;
+
+ return "Unknown Speed";
+}
+
+
+#define DBG_UDEV_INTEGER(name)\
+ dbg("%-20s = %x", to_string(name), (int) udev->name)
+
+#define DBG_UINF_INTEGER(name)\
+ dbg("%-20s = %x", to_string(name), (int) uinf->name)
+
+void dump_usb_interface(struct usbip_usb_interface *uinf)
+{
+ char buff[100];
+
+ usbip_names_get_class(buff, sizeof(buff),
+ uinf->bInterfaceClass,
+ uinf->bInterfaceSubClass,
+ uinf->bInterfaceProtocol);
+ dbg("%-20s = %s", "Interface(C/SC/P)", buff);
+}
+
+void dump_usb_device(struct usbip_usb_device *udev)
+{
+ char buff[100];
+
+ dbg("%-20s = %s", "path", udev->path);
+ dbg("%-20s = %s", "busid", udev->busid);
+
+ usbip_names_get_class(buff, sizeof(buff),
+ udev->bDeviceClass,
+ udev->bDeviceSubClass,
+ udev->bDeviceProtocol);
+ dbg("%-20s = %s", "Device(C/SC/P)", buff);
+
+ DBG_UDEV_INTEGER(bcdDevice);
+
+ usbip_names_get_product(buff, sizeof(buff),
+ udev->idVendor,
+ udev->idProduct);
+ dbg("%-20s = %s", "Vendor/Product", buff);
+
+ DBG_UDEV_INTEGER(bNumConfigurations);
+ DBG_UDEV_INTEGER(bNumInterfaces);
+
+ dbg("%-20s = %s", "speed",
+ usbip_speed_string(udev->speed));
+
+ DBG_UDEV_INTEGER(busnum);
+ DBG_UDEV_INTEGER(devnum);
+}
+
+
+int read_attr_value(struct udev_device *dev, const char *name,
+ const char *format)
+{
+ const char *attr;
+ int num = 0;
+ int ret;
+
+ attr = udev_device_get_sysattr_value(dev, name);
+ if (!attr) {
+ err("udev_device_get_sysattr_value failed");
+ goto err;
+ }
+
+ /* The client chooses the device configuration
+ * when attaching it so right after being bound
+ * to usbip-host on the server the device will
+ * have no configuration.
+ * Therefore, attributes such as bConfigurationValue
+ * and bNumInterfaces will not exist and sscanf will
+ * fail. Check for these cases and don't treat them
+ * as errors.
+ */
+
+ ret = sscanf(attr, format, &num);
+ if (ret < 1) {
+ if (strcmp(name, "bConfigurationValue") &&
+ strcmp(name, "bNumInterfaces")) {
+ err("sscanf failed for attribute %s", name);
+ goto err;
+ }
+ }
+
+err:
+
+ return num;
+}
+
+
+int read_attr_speed(struct udev_device *dev)
+{
+ const char *speed;
+
+ speed = udev_device_get_sysattr_value(dev, "speed");
+ if (!speed) {
+ err("udev_device_get_sysattr_value failed");
+ goto err;
+ }
+
+ for (int i = 0; speed_strings[i].speed != NULL; i++) {
+ if (!strcmp(speed, speed_strings[i].speed))
+ return speed_strings[i].num;
+ }
+
+err:
+
+ return USB_SPEED_UNKNOWN;
+}
+
+#define READ_ATTR(object, type, dev, name, format) \
+ do { \
+ (object)->name = (type) read_attr_value(dev, to_string(name), \
+ format); \
+ } while (0)
+
+
+int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev)
+{
+ uint32_t busnum, devnum;
+ const char *path, *name;
+
+ READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n");
+
+ READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n");
+ READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n");
+ READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n");
+
+ READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n");
+ READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n");
+
+ READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n");
+ udev->speed = read_attr_speed(sdev);
+
+ path = udev_device_get_syspath(sdev);
+ name = udev_device_get_sysname(sdev);
+
+ strncpy(udev->path, path, SYSFS_PATH_MAX);
+ strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE);
+
+ sscanf(name, "%u-%u", &busnum, &devnum);
+ udev->busnum = busnum;
+
+ return 0;
+}
+
+int read_usb_interface(struct usbip_usb_device *udev, int i,
+ struct usbip_usb_interface *uinf)
+{
+ char busid[SYSFS_BUS_ID_SIZE];
+ struct udev_device *sif;
+
+ sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i);
+
+ sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid);
+ if (!sif) {
+ err("udev_device_new_from_subsystem_sysname %s failed", busid);
+ return -1;
+ }
+
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n");
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n");
+ READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n");
+
+ return 0;
+}
+
+int usbip_names_init(char *f)
+{
+ return names_init(f);
+}
+
+void usbip_names_free(void)
+{
+ names_free();
+}
+
+void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
+ uint16_t product)
+{
+ const char *prod, *vend;
+
+ prod = names_product(vendor, product);
+ if (!prod)
+ prod = "unknown product";
+
+
+ vend = names_vendor(vendor);
+ if (!vend)
+ vend = "unknown vendor";
+
+ snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product);
+}
+
+void usbip_names_get_class(char *buff, size_t size, uint8_t class,
+ uint8_t subclass, uint8_t protocol)
+{
+ const char *c, *s, *p;
+
+ if (class == 0 && subclass == 0 && protocol == 0) {
+ snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol);
+ return;
+ }
+
+ p = names_protocol(class, subclass, protocol);
+ if (!p)
+ p = "unknown protocol";
+
+ s = names_subclass(class, subclass);
+ if (!s)
+ s = "unknown subclass";
+
+ c = names_class(class);
+ if (!c)
+ c = "unknown class";
+
+ snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
+}
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_common.h b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
new file mode 100644
index 00000000000..5a0e95edf4d
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_common.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_COMMON_H
+#define __USBIP_COMMON_H
+
+#include <libudev.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <syslog.h>
+#include <unistd.h>
+#include <linux/usb/ch9.h>
+#include "../../uapi/usbip.h"
+
+#ifndef USBIDS_FILE
+#define USBIDS_FILE "/usr/share/hwdata/usb.ids"
+#endif
+
+#ifndef VHCI_STATE_PATH
+#define VHCI_STATE_PATH "/var/run/vhci_hcd"
+#endif
+
+/* kernel module names */
+#define USBIP_CORE_MOD_NAME "usbip-core"
+#define USBIP_HOST_DRV_NAME "usbip-host"
+#define USBIP_VHCI_DRV_NAME "vhci_hcd"
+
+/* sysfs constants */
+#define SYSFS_MNT_PATH "/sys"
+#define SYSFS_BUS_NAME "bus"
+#define SYSFS_BUS_TYPE "usb"
+#define SYSFS_DRIVERS_NAME "drivers"
+
+#define SYSFS_PATH_MAX 256
+#define SYSFS_BUS_ID_SIZE 32
+
+extern int usbip_use_syslog;
+extern int usbip_use_stderr;
+extern int usbip_use_debug ;
+
+#define PROGNAME "usbip"
+
+#define pr_fmt(fmt) "%s: %s: " fmt "\n", PROGNAME
+#define dbg_fmt(fmt) pr_fmt("%s:%d:[%s] " fmt), "debug", \
+ __FILE__, __LINE__, __func__
+
+#define err(fmt, args...) \
+ do { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_ERR, pr_fmt(fmt), "error", ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, pr_fmt(fmt), "error", ##args); \
+ } \
+ } while (0)
+
+#define info(fmt, args...) \
+ do { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_INFO, pr_fmt(fmt), "info", ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, pr_fmt(fmt), "info", ##args); \
+ } \
+ } while (0)
+
+#define dbg(fmt, args...) \
+ do { \
+ if (usbip_use_debug) { \
+ if (usbip_use_syslog) { \
+ syslog(LOG_DEBUG, dbg_fmt(fmt), ##args); \
+ } \
+ if (usbip_use_stderr) { \
+ fprintf(stderr, dbg_fmt(fmt), ##args); \
+ } \
+ } \
+ } while (0)
+
+#define BUG() \
+ do { \
+ err("sorry, it's a bug!"); \
+ abort(); \
+ } while (0)
+
+struct usbip_usb_interface {
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t padding; /* alignment */
+} __attribute__((packed));
+
+struct usbip_usb_device {
+ char path[SYSFS_PATH_MAX];
+ char busid[SYSFS_BUS_ID_SIZE];
+
+ uint32_t busnum;
+ uint32_t devnum;
+ uint32_t speed;
+
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bConfigurationValue;
+ uint8_t bNumConfigurations;
+ uint8_t bNumInterfaces;
+} __attribute__((packed));
+
+#define to_string(s) #s
+
+void dump_usb_interface(struct usbip_usb_interface *);
+void dump_usb_device(struct usbip_usb_device *);
+int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev);
+int read_attr_value(struct udev_device *dev, const char *name,
+ const char *format);
+int read_usb_interface(struct usbip_usb_device *udev, int i,
+ struct usbip_usb_interface *uinf);
+
+const char *usbip_speed_string(int num);
+const char *usbip_status_string(int32_t status);
+
+int usbip_names_init(char *);
+void usbip_names_free(void);
+void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
+ uint16_t product);
+void usbip_names_get_class(char *buff, size_t size, uint8_t class,
+ uint8_t subclass, uint8_t protocol);
+
+#endif /* __USBIP_COMMON_H */
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
new file mode 100644
index 00000000000..92caef7474c
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <libudev.h>
+
+#include "usbip_common.h"
+#include "usbip_host_driver.h"
+#include "list.h"
+#include "sysfs_utils.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+struct usbip_host_driver *host_driver;
+struct udev *udev_context;
+
+static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
+{
+ char status_attr_path[SYSFS_PATH_MAX];
+ int fd;
+ int length;
+ char status;
+ int value = 0;
+
+ snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status",
+ udev->path);
+
+ if ((fd = open(status_attr_path, O_RDONLY)) < 0) {
+ err("error opening attribute %s", status_attr_path);
+ return -1;
+ }
+
+ length = read(fd, &status, 1);
+ if (length < 0) {
+ err("error reading attribute %s", status_attr_path);
+ close(fd);
+ return -1;
+ }
+
+ value = atoi(&status);
+
+ return value;
+}
+
+static
+struct usbip_exported_device *usbip_exported_device_new(const char *sdevpath)
+{
+ struct usbip_exported_device *edev = NULL;
+ struct usbip_exported_device *edev_old;
+ size_t size;
+ int i;
+
+ edev = calloc(1, sizeof(struct usbip_exported_device));
+
+ edev->sudev = udev_device_new_from_syspath(udev_context, sdevpath);
+ if (!edev->sudev) {
+ err("udev_device_new_from_syspath: %s", sdevpath);
+ goto err;
+ }
+
+ read_usb_device(edev->sudev, &edev->udev);
+
+ edev->status = read_attr_usbip_status(&edev->udev);
+ if (edev->status < 0)
+ goto err;
+
+ /* reallocate buffer to include usb interface data */
+ size = sizeof(struct usbip_exported_device) + edev->udev.bNumInterfaces *
+ sizeof(struct usbip_usb_interface);
+
+ edev_old = edev;
+ edev = realloc(edev, size);
+ if (!edev) {
+ edev = edev_old;
+ dbg("realloc failed");
+ goto err;
+ }
+
+ for (i = 0; i < edev->udev.bNumInterfaces; i++)
+ read_usb_interface(&edev->udev, i, &edev->uinf[i]);
+
+ return edev;
+err:
+ if (edev->sudev)
+ udev_device_unref(edev->sudev);
+ if (edev)
+ free(edev);
+
+ return NULL;
+}
+
+static int refresh_exported_devices(void)
+{
+ struct usbip_exported_device *edev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+ const char *path;
+ const char *driver;
+
+ enumerate = udev_enumerate_new(udev_context);
+ udev_enumerate_add_match_subsystem(enumerate, "usb");
+ udev_enumerate_scan_devices(enumerate);
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev_context, path);
+ if (dev == NULL)
+ continue;
+
+ /* Check whether device uses usbip-host driver. */
+ driver = udev_device_get_driver(dev);
+ if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) {
+ edev = usbip_exported_device_new(path);
+ if (!edev) {
+ dbg("usbip_exported_device_new failed");
+ continue;
+ }
+
+ list_add(&edev->node, &host_driver->edev_list);
+ host_driver->ndevs++;
+ }
+ }
+
+ return 0;
+}
+
+static void usbip_exported_device_destroy(void)
+{
+ struct list_head *i, *tmp;
+ struct usbip_exported_device *edev;
+
+ list_for_each_safe(i, tmp, &host_driver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ list_del(i);
+ free(edev);
+ }
+}
+
+int usbip_host_driver_open(void)
+{
+ int rc;
+
+ udev_context = udev_new();
+ if (!udev_context) {
+ err("udev_new failed");
+ return -1;
+ }
+
+ host_driver = calloc(1, sizeof(*host_driver));
+
+ host_driver->ndevs = 0;
+ INIT_LIST_HEAD(&host_driver->edev_list);
+
+ rc = refresh_exported_devices();
+ if (rc < 0)
+ goto err_free_host_driver;
+
+ return 0;
+
+err_free_host_driver:
+ free(host_driver);
+ host_driver = NULL;
+
+ udev_unref(udev_context);
+
+ return -1;
+}
+
+void usbip_host_driver_close(void)
+{
+ if (!host_driver)
+ return;
+
+ usbip_exported_device_destroy();
+
+ free(host_driver);
+ host_driver = NULL;
+
+ udev_unref(udev_context);
+}
+
+int usbip_host_refresh_device_list(void)
+{
+ int rc;
+
+ usbip_exported_device_destroy();
+
+ host_driver->ndevs = 0;
+ INIT_LIST_HEAD(&host_driver->edev_list);
+
+ rc = refresh_exported_devices();
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
+
+int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
+{
+ char attr_name[] = "usbip_sockfd";
+ char sockfd_attr_path[SYSFS_PATH_MAX];
+ char sockfd_buff[30];
+ int ret;
+
+ if (edev->status != SDEV_ST_AVAILABLE) {
+ dbg("device not available: %s", edev->udev.busid);
+ switch (edev->status) {
+ case SDEV_ST_ERROR:
+ dbg("status SDEV_ST_ERROR");
+ break;
+ case SDEV_ST_USED:
+ dbg("status SDEV_ST_USED");
+ break;
+ default:
+ dbg("status unknown: 0x%x", edev->status);
+ }
+ return -1;
+ }
+
+ /* only the first interface is true */
+ snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s",
+ edev->udev.path, attr_name);
+
+ snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
+
+ ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff,
+ strlen(sockfd_buff));
+ if (ret < 0) {
+ err("write_sysfs_attribute failed: sockfd %s to %s",
+ sockfd_buff, sockfd_attr_path);
+ return ret;
+ }
+
+ info("connect: %s", edev->udev.busid);
+
+ return ret;
+}
+
+struct usbip_exported_device *usbip_host_get_device(int num)
+{
+ struct list_head *i;
+ struct usbip_exported_device *edev;
+ int cnt = 0;
+
+ list_for_each(i, &host_driver->edev_list) {
+ edev = list_entry(i, struct usbip_exported_device, node);
+ if (num == cnt)
+ return edev;
+ else
+ cnt++;
+ }
+
+ return NULL;
+}
diff --git a/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
new file mode 100644
index 00000000000..2a31f855c61
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/usbip_host_driver.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __USBIP_HOST_DRIVER_H
+#define __USBIP_HOST_DRIVER_H
+
+#include <stdint.h>
+#include "usbip_common.h"
+#include "list.h"
+
+struct usbip_host_driver {
+ int ndevs;
+ /* list of exported device */
+ struct list_head edev_list;
+};
+
+struct usbip_exported_device {
+ struct udev_device *sudev;
+ int32_t status;
+ struct usbip_usb_device udev;
+ struct list_head node;
+ struct usbip_usb_interface uinf[];
+};
+
+extern struct usbip_host_driver *host_driver;
+
+int usbip_host_driver_open(void);
+void usbip_host_driver_close(void);
+
+int usbip_host_refresh_device_list(void);
+int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd);
+struct usbip_exported_device *usbip_host_get_device(int num);
+
+#endif /* __USBIP_HOST_DRIVER_H */
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.c b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
new file mode 100644
index 00000000000..ad920477353
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#include "usbip_common.h"
+#include "vhci_driver.h"
+#include <limits.h>
+#include <netdb.h>
+#include <libudev.h>
+#include "sysfs_utils.h"
+
+#undef PROGNAME
+#define PROGNAME "libusbip"
+
+struct usbip_vhci_driver *vhci_driver;
+struct udev *udev_context;
+
+static struct usbip_imported_device *
+imported_device_init(struct usbip_imported_device *idev, char *busid)
+{
+ struct udev_device *sudev;
+
+ sudev = udev_device_new_from_subsystem_sysname(udev_context,
+ "usb", busid);
+ if (!sudev) {
+ dbg("udev_device_new_from_subsystem_sysname failed: %s", busid);
+ goto err;
+ }
+ read_usb_device(sudev, &idev->udev);
+ udev_device_unref(sudev);
+
+ return idev;
+
+err:
+ return NULL;
+}
+
+
+
+static int parse_status(const char *value)
+{
+ int ret = 0;
+ char *c;
+
+
+ for (int i = 0; i < vhci_driver->nports; i++)
+ memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i]));
+
+
+ /* skip a header line */
+ c = strchr(value, '\n');
+ if (!c)
+ return -1;
+ c++;
+
+ while (*c != '\0') {
+ int port, status, speed, devid;
+ unsigned long socket;
+ char lbusid[SYSFS_BUS_ID_SIZE];
+
+ ret = sscanf(c, "%d %d %d %x %lx %31s\n",
+ &port, &status, &speed,
+ &devid, &socket, lbusid);
+
+ if (ret < 5) {
+ dbg("sscanf failed: %d", ret);
+ BUG();
+ }
+
+ dbg("port %d status %d speed %d devid %x",
+ port, status, speed, devid);
+ dbg("socket %lx lbusid %s", socket, lbusid);
+
+
+ /* if a device is connected, look at it */
+ {
+ struct usbip_imported_device *idev = &vhci_driver->idev[port];
+
+ idev->port = port;
+ idev->status = status;
+
+ idev->devid = devid;
+
+ idev->busnum = (devid >> 16);
+ idev->devnum = (devid & 0x0000ffff);
+
+ if (idev->status != VDEV_ST_NULL
+ && idev->status != VDEV_ST_NOTASSIGNED) {
+ idev = imported_device_init(idev, lbusid);
+ if (!idev) {
+ dbg("imported_device_init failed");
+ return -1;
+ }
+ }
+ }
+
+
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ break;
+ c++;
+ }
+
+ dbg("exit");
+
+ return 0;
+}
+
+static int refresh_imported_device_list(void)
+{
+ const char *attr_status;
+
+ attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
+ "status");
+ if (!attr_status) {
+ err("udev_device_get_sysattr_value failed");
+ return -1;
+ }
+
+ return parse_status(attr_status);
+}
+
+static int get_nports(void)
+{
+ char *c;
+ int nports = 0;
+ const char *attr_status;
+
+ attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device,
+ "status");
+ if (!attr_status) {
+ err("udev_device_get_sysattr_value failed");
+ return -1;
+ }
+
+ /* skip a header line */
+ c = strchr(attr_status, '\n');
+ if (!c)
+ return 0;
+ c++;
+
+ while (*c != '\0') {
+ /* go to the next line */
+ c = strchr(c, '\n');
+ if (!c)
+ return nports;
+ c++;
+ nports += 1;
+ }
+
+ return nports;
+}
+
+/*
+ * Read the given port's record.
+ *
+ * To avoid buffer overflow we will read the entire line and
+ * validate each part's size. The initial buffer is padded by 4 to
+ * accommodate the 2 spaces, 1 newline and an additional character
+ * which is needed to properly validate the 3rd part without it being
+ * truncated to an acceptable length.
+ */
+static int read_record(int rhport, char *host, unsigned long host_len,
+ char *port, unsigned long port_len, char *busid)
+{
+ int part;
+ FILE *file;
+ char path[PATH_MAX+1];
+ char *buffer, *start, *end;
+ char delim[] = {' ', ' ', '\n'};
+ int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE};
+ size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4;
+
+ buffer = malloc(buffer_len);
+ if (!buffer)
+ return -1;
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
+
+ file = fopen(path, "r");
+ if (!file) {
+ err("fopen");
+ free(buffer);
+ return -1;
+ }
+
+ if (fgets(buffer, buffer_len, file) == NULL) {
+ err("fgets");
+ free(buffer);
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+
+ /* validate the length of each of the 3 parts */
+ start = buffer;
+ for (part = 0; part < 3; part++) {
+ end = strchr(start, delim[part]);
+ if (end == NULL || (end - start) > max_len[part]) {
+ free(buffer);
+ return -1;
+ }
+ start = end + 1;
+ }
+
+ if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) {
+ err("sscanf");
+ free(buffer);
+ return -1;
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int usbip_vhci_driver_open(void)
+{
+ udev_context = udev_new();
+ if (!udev_context) {
+ err("udev_new failed");
+ return -1;
+ }
+
+ vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver));
+
+ /* will be freed in usbip_driver_close() */
+ vhci_driver->hc_device =
+ udev_device_new_from_subsystem_sysname(udev_context,
+ USBIP_VHCI_BUS_TYPE,
+ USBIP_VHCI_DRV_NAME);
+ if (!vhci_driver->hc_device) {
+ err("udev_device_new_from_subsystem_sysname failed");
+ goto err;
+ }
+
+ vhci_driver->nports = get_nports();
+
+ dbg("available ports: %d", vhci_driver->nports);
+
+ if (refresh_imported_device_list())
+ goto err;
+
+ return 0;
+
+err:
+ udev_device_unref(vhci_driver->hc_device);
+
+ if (vhci_driver)
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+
+ udev_unref(udev_context);
+
+ return -1;
+}
+
+
+void usbip_vhci_driver_close(void)
+{
+ if (!vhci_driver)
+ return;
+
+ udev_device_unref(vhci_driver->hc_device);
+
+ free(vhci_driver);
+
+ vhci_driver = NULL;
+
+ udev_unref(udev_context);
+}
+
+
+int usbip_vhci_refresh_device_list(void)
+{
+
+ if (refresh_imported_device_list())
+ goto err;
+
+ return 0;
+err:
+ dbg("failed to refresh device list");
+ return -1;
+}
+
+
+int usbip_vhci_get_free_port(void)
+{
+ for (int i = 0; i < vhci_driver->nports; i++) {
+ if (vhci_driver->idev[i].status == VDEV_ST_NULL)
+ return i;
+ }
+
+ return -1;
+}
+
+int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
+ uint32_t speed) {
+ char buff[200]; /* what size should be ? */
+ char attach_attr_path[SYSFS_PATH_MAX];
+ char attr_attach[] = "attach";
+ const char *path;
+ int ret;
+
+ snprintf(buff, sizeof(buff), "%u %d %u %u",
+ port, sockfd, devid, speed);
+ dbg("writing: %s", buff);
+
+ path = udev_device_get_syspath(vhci_driver->hc_device);
+ snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s",
+ path, attr_attach);
+ dbg("attach attribute path: %s", attach_attr_path);
+
+ ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff));
+ if (ret < 0) {
+ dbg("write_sysfs_attribute failed");
+ return -1;
+ }
+
+ dbg("attached port: %d", port);
+
+ return 0;
+}
+
+static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
+{
+ return (busnum << 16) | devnum;
+}
+
+/* will be removed */
+int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+ uint8_t devnum, uint32_t speed)
+{
+ int devid = get_devid(busnum, devnum);
+
+ return usbip_vhci_attach_device2(port, sockfd, devid, speed);
+}
+
+int usbip_vhci_detach_device(uint8_t port)
+{
+ char detach_attr_path[SYSFS_PATH_MAX];
+ char attr_detach[] = "detach";
+ char buff[200]; /* what size should be ? */
+ const char *path;
+ int ret;
+
+ snprintf(buff, sizeof(buff), "%u", port);
+ dbg("writing: %s", buff);
+
+ path = udev_device_get_syspath(vhci_driver->hc_device);
+ snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s",
+ path, attr_detach);
+ dbg("detach attribute path: %s", detach_attr_path);
+
+ ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff));
+ if (ret < 0) {
+ dbg("write_sysfs_attribute failed");
+ return -1;
+ }
+
+ dbg("detached port: %d", port);
+
+ return 0;
+}
+
+int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev)
+{
+ char product_name[100];
+ char host[NI_MAXHOST] = "unknown host";
+ char serv[NI_MAXSERV] = "unknown port";
+ char remote_busid[SYSFS_BUS_ID_SIZE];
+ int ret;
+ int read_record_error = 0;
+
+ if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED)
+ return 0;
+
+ ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv),
+ remote_busid);
+ if (ret) {
+ err("read_record");
+ read_record_error = 1;
+ }
+
+ printf("Port %02d: <%s> at %s\n", idev->port,
+ usbip_status_string(idev->status),
+ usbip_speed_string(idev->udev.speed));
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ idev->udev.idVendor, idev->udev.idProduct);
+
+ printf(" %s\n", product_name);
+
+ if (!read_record_error) {
+ printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid,
+ host, serv, remote_busid);
+ printf("%10s -> remote bus/dev %03d/%03d\n", " ",
+ idev->busnum, idev->devnum);
+ } else {
+ printf("%10s -> unknown host, remote port and remote busid\n",
+ idev->udev.busid);
+ printf("%10s -> remote bus/dev %03d/%03d\n", " ",
+ idev->busnum, idev->devnum);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/usbip/userspace/libsrc/vhci_driver.h b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
new file mode 100644
index 00000000000..fa2316cf2ca
--- /dev/null
+++ b/drivers/staging/usbip/userspace/libsrc/vhci_driver.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __VHCI_DRIVER_H
+#define __VHCI_DRIVER_H
+
+#include <libudev.h>
+#include <stdint.h>
+
+#include "usbip_common.h"
+
+#define USBIP_VHCI_BUS_TYPE "platform"
+#define MAXNPORT 128
+
+struct usbip_imported_device {
+ uint8_t port;
+ uint32_t status;
+
+ uint32_t devid;
+
+ uint8_t busnum;
+ uint8_t devnum;
+
+ /* usbip_class_device list */
+ struct usbip_usb_device udev;
+};
+
+struct usbip_vhci_driver {
+
+ /* /sys/devices/platform/vhci_hcd */
+ struct udev_device *hc_device;
+
+ int nports;
+ struct usbip_imported_device idev[MAXNPORT];
+};
+
+
+extern struct usbip_vhci_driver *vhci_driver;
+
+int usbip_vhci_driver_open(void);
+void usbip_vhci_driver_close(void);
+
+int usbip_vhci_refresh_device_list(void);
+
+
+int usbip_vhci_get_free_port(void);
+int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
+ uint32_t speed);
+
+/* will be removed */
+int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
+ uint8_t devnum, uint32_t speed);
+
+int usbip_vhci_detach_device(uint8_t port);
+
+int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev);
+
+#endif /* __VHCI_DRIVER_H */
diff --git a/drivers/staging/usbip/userspace/src/Makefile.am b/drivers/staging/usbip/userspace/src/Makefile.am
new file mode 100644
index 00000000000..e81a4ebadef
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/Makefile.am
@@ -0,0 +1,11 @@
+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
+
+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
+
+usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c
diff --git a/drivers/staging/usbip/userspace/src/usbip.c b/drivers/staging/usbip/userspace/src/usbip.c
new file mode 100644
index 00000000000..d7599d94352
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip.c
@@ -0,0 +1,201 @@
+/*
+ * command structure borrowed from udev
+ * (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
+ *
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <getopt.h>
+#include <syslog.h>
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static int usbip_help(int argc, char *argv[]);
+static int usbip_version(int argc, char *argv[]);
+
+static const char usbip_version_string[] = PACKAGE_STRING;
+
+static const char usbip_usage_string[] =
+ "usbip [--debug] [--log] [--tcp-port PORT] [version]\n"
+ " [help] <command> <args>\n";
+
+static void usbip_usage(void)
+{
+ printf("usage: %s", usbip_usage_string);
+}
+
+struct command {
+ const char *name;
+ int (*fn)(int argc, char *argv[]);
+ const char *help;
+ void (*usage)(void);
+};
+
+static const struct command cmds[] = {
+ {
+ .name = "help",
+ .fn = usbip_help,
+ .help = NULL,
+ .usage = NULL
+ },
+ {
+ .name = "version",
+ .fn = usbip_version,
+ .help = NULL,
+ .usage = NULL
+ },
+ {
+ .name = "attach",
+ .fn = usbip_attach,
+ .help = "Attach a remote USB device",
+ .usage = usbip_attach_usage
+ },
+ {
+ .name = "detach",
+ .fn = usbip_detach,
+ .help = "Detach a remote USB device",
+ .usage = usbip_detach_usage
+ },
+ {
+ .name = "list",
+ .fn = usbip_list,
+ .help = "List exportable or local USB devices",
+ .usage = usbip_list_usage
+ },
+ {
+ .name = "bind",
+ .fn = usbip_bind,
+ .help = "Bind device to " USBIP_HOST_DRV_NAME ".ko",
+ .usage = usbip_bind_usage
+ },
+ {
+ .name = "unbind",
+ .fn = usbip_unbind,
+ .help = "Unbind device from " USBIP_HOST_DRV_NAME ".ko",
+ .usage = usbip_unbind_usage
+ },
+ {
+ .name = "port",
+ .fn = usbip_port_show,
+ .help = "Show imported USB devices",
+ .usage = NULL
+ },
+ { NULL, NULL, NULL, NULL }
+};
+
+static int usbip_help(int argc, char *argv[])
+{
+ const struct command *cmd;
+ int i;
+ int ret = 0;
+
+ if (argc > 1 && argv++) {
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) {
+ cmds[i].usage();
+ goto done;
+ }
+ ret = -1;
+ }
+
+ usbip_usage();
+ printf("\n");
+ for (cmd = cmds; cmd->name != NULL; cmd++)
+ if (cmd->help != NULL)
+ printf(" %-10s %s\n", cmd->name, cmd->help);
+ printf("\n");
+done:
+ return ret;
+}
+
+static int usbip_version(int argc, char *argv[])
+{
+ (void) argc;
+ (void) argv;
+
+ printf(PROGNAME " (%s)\n", usbip_version_string);
+ return 0;
+}
+
+static int run_command(const struct command *cmd, int argc, char *argv[])
+{
+ dbg("running command: `%s'", cmd->name);
+ return cmd->fn(argc, argv);
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "debug", no_argument, NULL, 'd' },
+ { "log", no_argument, NULL, 'l' },
+ { "tcp-port", required_argument, NULL, 't' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ char *cmd;
+ int opt;
+ int i, rc = -1;
+
+ usbip_use_stderr = 1;
+ opterr = 0;
+ for (;;) {
+ opt = getopt_long(argc, argv, "+dlt:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'd':
+ usbip_use_debug = 1;
+ break;
+ case 'l':
+ usbip_use_syslog = 1;
+ openlog("", LOG_PID, LOG_USER);
+ break;
+ case 't':
+ usbip_setup_port_number(optarg);
+ break;
+ case '?':
+ printf("usbip: invalid option\n");
+ default:
+ usbip_usage();
+ goto out;
+ }
+ }
+
+ cmd = argv[optind];
+ if (cmd) {
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (!strcmp(cmds[i].name, cmd)) {
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+ rc = run_command(&cmds[i], argc, argv);
+ goto out;
+ }
+ }
+
+ /* invalid command */
+ usbip_help(0, NULL);
+out:
+ return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip.h b/drivers/staging/usbip/userspace/src/usbip.h
new file mode 100644
index 00000000000..84fe66a9d8a
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __USBIP_H
+#define __USBIP_H
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+/* usbip commands */
+int usbip_attach(int argc, char *argv[]);
+int usbip_detach(int argc, char *argv[]);
+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[]);
+
+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);
+
+#endif /* __USBIP_H */
diff --git a/drivers/staging/usbip/userspace/src/usbip_attach.c b/drivers/staging/usbip/userspace/src/usbip_attach.c
new file mode 100644
index 00000000000..d58a14dfc09
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_attach.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_attach_usage_string[] =
+ "usbip attach <args>\n"
+ " -r, --remote=<host> The machine with exported USB devices\n"
+ " -b, --busid=<busid> Busid of the device on <host>\n";
+
+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;
+ int port;
+
+ rc = usbip_vhci_driver_open();
+ if (rc < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ port = usbip_vhci_get_free_port();
+ if (port < 0) {
+ err("no free port");
+ usbip_vhci_driver_close();
+ return -1;
+ }
+
+ rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
+ udev->devnum, udev->speed);
+ if (rc < 0) {
+ err("import device");
+ usbip_vhci_driver_close();
+ return -1;
+ }
+
+ usbip_vhci_driver_close();
+
+ return port;
+}
+
+static int query_import_device(int sockfd, char *busid)
+{
+ int rc;
+ struct op_import_request request;
+ struct op_import_reply reply;
+ uint16_t code = OP_REP_IMPORT;
+
+ memset(&request, 0, sizeof(request));
+ memset(&reply, 0, sizeof(reply));
+
+ /* send a request */
+ rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
+ if (rc < 0) {
+ err("send op_common");
+ return -1;
+ }
+
+ strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
+
+ PACK_OP_IMPORT_REQUEST(0, &request);
+
+ rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
+ if (rc < 0) {
+ err("send op_import_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_import_reply");
+ return -1;
+ }
+
+ PACK_OP_IMPORT_REPLY(0, &reply);
+
+ /* check the reply */
+ if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
+ err("recv different busid %s", reply.udev.busid);
+ return -1;
+ }
+
+ /* import a device */
+ return import_device(sockfd, &reply.udev);
+}
+
+static int attach_device(char *host, char *busid)
+{
+ int sockfd;
+ int rc;
+ int rhport;
+
+ sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+ if (sockfd < 0) {
+ err("tcp connect");
+ return -1;
+ }
+
+ rhport = query_import_device(sockfd, busid);
+ if (rhport < 0) {
+ err("query");
+ return -1;
+ }
+
+ close(sockfd);
+
+ rc = record_connection(host, usbip_port_string, busid, rhport);
+ if (rc < 0) {
+ err("record connection");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usbip_attach(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 = attach_device(host, busid);
+ goto out;
+
+err_out:
+ usbip_attach_usage();
+out:
+ return ret;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_bind.c b/drivers/staging/usbip/userspace/src/usbip_bind.c
new file mode 100644
index 00000000000..fa46141ae68
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_bind.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "usbip.h"
+#include "sysfs_utils.h"
+
+enum unbind_status {
+ UNBIND_ST_OK,
+ UNBIND_ST_USBIP_HOST,
+ UNBIND_ST_FAILED
+};
+
+static const char usbip_bind_usage_string[] =
+ "usbip bind <args>\n"
+ " -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device "
+ "on <busid>\n";
+
+void usbip_bind_usage(void)
+{
+ printf("usage: %s", usbip_bind_usage_string);
+}
+
+/* call at unbound state */
+static int bind_usbip(char *busid)
+{
+ char attr_name[] = "bind";
+ char bind_attr_path[SYSFS_PATH_MAX];
+ int rc = -1;
+
+ snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
+ SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name);
+
+ rc = write_sysfs_attribute(bind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error binding device %s to driver: %s", busid,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* buggy driver may cause dead lock */
+static int unbind_other(char *busid)
+{
+ enum unbind_status status = UNBIND_ST_OK;
+
+ char attr_name[] = "unbind";
+ char unbind_attr_path[SYSFS_PATH_MAX];
+ int rc = -1;
+
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *driver;
+ const char *bDevClass;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Get the device. */
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ dbg("unable to find device with bus ID %s", busid);
+ goto err_close_busid_dev;
+ }
+
+ /* Check what kind of device it is. */
+ bDevClass = udev_device_get_sysattr_value(dev, "bDeviceClass");
+ if (!bDevClass) {
+ dbg("unable to get bDevClass device attribute");
+ goto err_close_busid_dev;
+ }
+
+ if (!strncmp(bDevClass, "09", strlen(bDevClass))) {
+ dbg("skip unbinding of hub");
+ goto err_close_busid_dev;
+ }
+
+ /* Get the device driver. */
+ driver = udev_device_get_driver(dev);
+ if (!driver) {
+ /* No driver bound to this device. */
+ goto out;
+ }
+
+ if (!strncmp(USBIP_HOST_DRV_NAME, driver,
+ strlen(USBIP_HOST_DRV_NAME))) {
+ /* Already bound to usbip-host. */
+ status = UNBIND_ST_USBIP_HOST;
+ goto out;
+ }
+
+ /* Unbind device from driver. */
+ snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE,
+ SYSFS_DRIVERS_NAME, driver, attr_name);
+
+ rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error unbinding device %s from driver", busid);
+ goto err_close_busid_dev;
+ }
+
+ goto out;
+
+err_close_busid_dev:
+ status = UNBIND_ST_FAILED;
+out:
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return status;
+}
+
+static int bind_device(char *busid)
+{
+ int rc;
+ struct udev *udev;
+ struct udev_device *dev;
+
+ /* Check whether the device with this bus ID exists. */
+ udev = udev_new();
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ err("device with the specified bus ID does not exist");
+ return -1;
+ }
+ udev_unref(udev);
+
+ rc = unbind_other(busid);
+ if (rc == UNBIND_ST_FAILED) {
+ err("could not unbind driver from device on busid %s", busid);
+ return -1;
+ } else if (rc == UNBIND_ST_USBIP_HOST) {
+ err("device on busid %s is already bound to %s", busid,
+ USBIP_HOST_DRV_NAME);
+ return -1;
+ }
+
+ rc = modify_match_busid(busid, 1);
+ if (rc < 0) {
+ err("unable to bind device on %s", busid);
+ return -1;
+ }
+
+ rc = bind_usbip(busid);
+ if (rc < 0) {
+ err("could not bind device to %s", USBIP_HOST_DRV_NAME);
+ modify_match_busid(busid, 0);
+ return -1;
+ }
+
+ info("bind device on busid %s: complete", busid);
+
+ return 0;
+}
+
+int usbip_bind(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "busid", required_argument, NULL, 'b' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'b':
+ ret = bind_device(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_bind_usage();
+out:
+ return ret;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_detach.c b/drivers/staging/usbip/userspace/src/usbip_detach.c
new file mode 100644
index 00000000000..05c6d15856e
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_detach.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_detach_usage_string[] =
+ "usbip detach <args>\n"
+ " -p, --port=<port> " USBIP_VHCI_DRV_NAME
+ " port the device is on\n";
+
+void usbip_detach_usage(void)
+{
+ printf("usage: %s", usbip_detach_usage_string);
+}
+
+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])) {
+ err("invalid port %s", port);
+ return -1;
+ }
+
+ /* check max port */
+
+ portnum = atoi(port);
+
+ /* remove the port state file */
+
+ snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum);
+
+ remove(path);
+ rmdir(VHCI_STATE_PATH);
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ ret = usbip_vhci_detach_device(portnum);
+ if (ret < 0)
+ return -1;
+
+ usbip_vhci_driver_close();
+
+ return ret;
+}
+
+int usbip_detach(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "port", required_argument, NULL, 'p' },
+ { NULL, 0, NULL, 0 }
+ };
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "p:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'p':
+ ret = detach_port(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_detach_usage();
+out:
+ return ret;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_list.c b/drivers/staging/usbip/userspace/src/usbip_list.c
new file mode 100644
index 00000000000..d5ce34a410e
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_list.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <getopt.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+#include "usbip.h"
+
+static const char usbip_list_usage_string[] =
+ "usbip list [-p|--parsable] <args>\n"
+ " -p, --parsable Parsable list format\n"
+ " -r, --remote=<host> List the exportable USB devices on <host>\n"
+ " -l, --local List the local USB devices\n";
+
+void usbip_list_usage(void)
+{
+ printf("usage: %s", usbip_list_usage_string);
+}
+
+static int get_exported_devices(char *host, int sockfd)
+{
+ char product_name[100];
+ char class_name[100];
+ struct op_devlist_reply reply;
+ uint16_t code = OP_REP_DEVLIST;
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uintf;
+ unsigned int i;
+ int rc, j;
+
+ rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
+ if (rc < 0) {
+ dbg("usbip_net_send_op_common failed");
+ return -1;
+ }
+
+ rc = usbip_net_recv_op_common(sockfd, &code);
+ if (rc < 0) {
+ dbg("usbip_net_recv_op_common failed");
+ return -1;
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
+ if (rc < 0) {
+ dbg("usbip_net_recv_op_devlist failed");
+ return -1;
+ }
+ PACK_OP_DEVLIST_REPLY(0, &reply);
+ dbg("exportable devices: %d\n", reply.ndev);
+
+ if (reply.ndev == 0) {
+ info("no exportable devices found on %s", host);
+ return 0;
+ }
+
+ printf("Exportable USB devices\n");
+ printf("======================\n");
+ printf(" - %s\n", host);
+
+ for (i = 0; i < reply.ndev; i++) {
+ memset(&udev, 0, sizeof(udev));
+ rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
+ return -1;
+ }
+ usbip_net_pack_usb_device(0, &udev);
+
+ usbip_names_get_product(product_name, sizeof(product_name),
+ udev.idVendor, udev.idProduct);
+ usbip_names_get_class(class_name, sizeof(class_name),
+ udev.bDeviceClass, udev.bDeviceSubClass,
+ udev.bDeviceProtocol);
+ printf("%11s: %s\n", udev.busid, product_name);
+ printf("%11s: %s\n", "", udev.path);
+ printf("%11s: %s\n", "", class_name);
+
+ for (j = 0; j < udev.bNumInterfaces; j++) {
+ rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
+ if (rc < 0) {
+ err("usbip_net_recv failed: usbip_usb_intf[%d]",
+ j);
+
+ return -1;
+ }
+ usbip_net_pack_usb_interface(0, &uintf);
+
+ usbip_names_get_class(class_name, sizeof(class_name),
+ uintf.bInterfaceClass,
+ uintf.bInterfaceSubClass,
+ uintf.bInterfaceProtocol);
+ printf("%11s: %2d - %s\n", "", j, class_name);
+ }
+
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int list_exported_devices(char *host)
+{
+ int rc;
+ int sockfd;
+
+ sockfd = usbip_net_tcp_connect(host, usbip_port_string);
+ if (sockfd < 0) {
+ err("could not connect to %s:%s: %s", host,
+ usbip_port_string, gai_strerror(sockfd));
+ return -1;
+ }
+ dbg("connected to %s:%s", host, usbip_port_string);
+
+ rc = get_exported_devices(host, sockfd);
+ if (rc < 0) {
+ err("failed to get device list from %s", host);
+ return -1;
+ }
+
+ close(sockfd);
+
+ return 0;
+}
+
+static void print_device(const char *busid, const char *vendor,
+ const char *product, bool parsable)
+{
+ if (parsable)
+ printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product);
+ else
+ printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product);
+}
+
+static void print_product_name(char *product_name, bool parsable)
+{
+ if (!parsable)
+ printf(" %s\n", product_name);
+}
+
+static int list_devices(bool parsable)
+{
+ struct udev *udev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ struct udev_device *dev;
+ const char *path;
+ const char *idVendor;
+ const char *idProduct;
+ const char *bConfValue;
+ const char *bNumIntfs;
+ const char *busid;
+ char product_name[128];
+ int ret = -1;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Create libudev device enumeration. */
+ enumerate = udev_enumerate_new(udev);
+
+ /* Take only USB devices that are not hubs and do not have
+ * the bInterfaceNumber attribute, i.e. are not interfaces.
+ */
+ udev_enumerate_add_match_subsystem(enumerate, "usb");
+ udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass", "09");
+ udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber", NULL);
+ udev_enumerate_scan_devices(enumerate);
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+
+ /* Show information about each device. */
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+
+ /* Get device information. */
+ idVendor = udev_device_get_sysattr_value(dev, "idVendor");
+ idProduct = udev_device_get_sysattr_value(dev, "idProduct");
+ bConfValue = udev_device_get_sysattr_value(dev, "bConfigurationValue");
+ bNumIntfs = udev_device_get_sysattr_value(dev, "bNumInterfaces");
+ busid = udev_device_get_sysname(dev);
+ if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) {
+ err("problem getting device attributes: %s",
+ strerror(errno));
+ goto err_out;
+ }
+
+ /* Get product name. */
+ usbip_names_get_product(product_name, sizeof(product_name),
+ strtol(idVendor, NULL, 16),
+ strtol(idProduct, NULL, 16));
+
+ /* Print information. */
+ print_device(busid, idVendor, idProduct, parsable);
+ print_product_name(product_name, parsable);
+
+ printf("\n");
+
+ udev_device_unref(dev);
+ }
+
+ ret = 0;
+
+err_out:
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+
+ return ret;
+}
+
+int usbip_list(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "parsable", no_argument, NULL, 'p' },
+ { "remote", required_argument, NULL, 'r' },
+ { "local", no_argument, NULL, 'l' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ bool parsable = false;
+ int opt;
+ int ret = -1;
+
+ if (usbip_names_init(USBIDS_FILE))
+ err("failed to open %s", USBIDS_FILE);
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "pr:l", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'p':
+ parsable = true;
+ break;
+ case 'r':
+ ret = list_exported_devices(optarg);
+ goto out;
+ case 'l':
+ ret = list_devices(parsable);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_list_usage();
+out:
+ usbip_names_free();
+
+ return ret;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.c b/drivers/staging/usbip/userspace/src/usbip_network.c
new file mode 100644
index 00000000000..b4c37e76a6e
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_network.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/socket.h>
+
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include "usbip_common.h"
+#include "usbip_network.h"
+
+int usbip_port = 3240;
+char *usbip_port_string = "3240";
+
+void usbip_setup_port_number(char *arg)
+{
+ dbg("parsing port arg '%s'", arg);
+ char *end;
+ unsigned long int port = strtoul(arg, &end, 10);
+
+ if (end == arg) {
+ err("port: could not parse '%s' as a decimal integer", arg);
+ return;
+ }
+
+ if (*end != '\0') {
+ err("port: garbage at end of '%s'", arg);
+ return;
+ }
+
+ if (port > UINT16_MAX) {
+ err("port: %s too high (max=%d)",
+ arg, UINT16_MAX);
+ return;
+ }
+
+ usbip_port = port;
+ usbip_port_string = arg;
+ info("using port %d (\"%s\")", usbip_port, usbip_port_string);
+}
+
+void usbip_net_pack_uint32_t(int pack, uint32_t *num)
+{
+ uint32_t i;
+
+ if (pack)
+ i = htonl(*num);
+ else
+ i = ntohl(*num);
+
+ *num = i;
+}
+
+void usbip_net_pack_uint16_t(int pack, uint16_t *num)
+{
+ uint16_t i;
+
+ if (pack)
+ i = htons(*num);
+ else
+ i = ntohs(*num);
+
+ *num = i;
+}
+
+void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev)
+{
+ usbip_net_pack_uint32_t(pack, &udev->busnum);
+ usbip_net_pack_uint32_t(pack, &udev->devnum);
+ usbip_net_pack_uint32_t(pack, &udev->speed);
+
+ usbip_net_pack_uint16_t(pack, &udev->idVendor);
+ usbip_net_pack_uint16_t(pack, &udev->idProduct);
+ usbip_net_pack_uint16_t(pack, &udev->bcdDevice);
+}
+
+void usbip_net_pack_usb_interface(int pack __attribute__((unused)),
+ struct usbip_usb_interface *udev
+ __attribute__((unused)))
+{
+ /* uint8_t members need nothing */
+}
+
+static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
+ int sending)
+{
+ ssize_t nbytes;
+ ssize_t total = 0;
+
+ if (!bufflen)
+ return 0;
+
+ do {
+ if (sending)
+ nbytes = send(sockfd, buff, bufflen, 0);
+ else
+ nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
+
+ if (nbytes <= 0)
+ return -1;
+
+ buff = (void *)((intptr_t) buff + nbytes);
+ bufflen -= nbytes;
+ total += nbytes;
+
+ } while (bufflen > 0);
+
+ return total;
+}
+
+ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)
+{
+ return usbip_net_xmit(sockfd, buff, bufflen, 0);
+}
+
+ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)
+{
+ return usbip_net_xmit(sockfd, buff, bufflen, 1);
+}
+
+int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
+{
+ struct op_common op_common;
+ int rc;
+
+ memset(&op_common, 0, sizeof(op_common));
+
+ op_common.version = USBIP_VERSION;
+ op_common.code = code;
+ op_common.status = status;
+
+ PACK_OP_COMMON(1, &op_common);
+
+ rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));
+ if (rc < 0) {
+ dbg("usbip_net_send failed: %d", rc);
+ return -1;
+ }
+
+ return 0;
+}
+
+int usbip_net_recv_op_common(int sockfd, uint16_t *code)
+{
+ struct op_common op_common;
+ int rc;
+
+ memset(&op_common, 0, sizeof(op_common));
+
+ rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
+ if (rc < 0) {
+ dbg("usbip_net_recv failed: %d", rc);
+ goto err;
+ }
+
+ PACK_OP_COMMON(0, &op_common);
+
+ if (op_common.version != USBIP_VERSION) {
+ dbg("version mismatch: %d %d", op_common.version,
+ USBIP_VERSION);
+ goto err;
+ }
+
+ switch (*code) {
+ case OP_UNSPEC:
+ break;
+ default:
+ if (op_common.code != *code) {
+ dbg("unexpected pdu %#0x for %#0x", op_common.code,
+ *code);
+ goto err;
+ }
+ }
+
+ if (op_common.status != ST_OK) {
+ dbg("request failed at peer: %d", op_common.status);
+ goto err;
+ }
+
+ *code = op_common.code;
+
+ return 0;
+err:
+ return -1;
+}
+
+int usbip_net_set_reuseaddr(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: SO_REUSEADDR");
+
+ return ret;
+}
+
+int usbip_net_set_nodelay(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: TCP_NODELAY");
+
+ return ret;
+}
+
+int usbip_net_set_keepalive(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: SO_KEEPALIVE");
+
+ return ret;
+}
+
+int usbip_net_set_v6only(int sockfd)
+{
+ const int val = 1;
+ int ret;
+
+ ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+ if (ret < 0)
+ dbg("setsockopt: IPV6_V6ONLY");
+
+ return ret;
+}
+
+/*
+ * IPv6 Ready
+ */
+int usbip_net_tcp_connect(char *hostname, char *service)
+{
+ struct addrinfo hints, *res, *rp;
+ int sockfd;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* get all possible addresses */
+ ret = getaddrinfo(hostname, service, &hints, &res);
+ if (ret < 0) {
+ dbg("getaddrinfo: %s service %s: %s", hostname, service,
+ gai_strerror(ret));
+ return ret;
+ }
+
+ /* try the addresses */
+ for (rp = res; rp; rp = rp->ai_next) {
+ sockfd = socket(rp->ai_family, rp->ai_socktype,
+ rp->ai_protocol);
+ if (sockfd < 0)
+ continue;
+
+ /* should set TCP_NODELAY for usbip */
+ usbip_net_set_nodelay(sockfd);
+ /* TODO: write code for heartbeat */
+ usbip_net_set_keepalive(sockfd);
+
+ if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+
+ close(sockfd);
+ }
+
+ freeaddrinfo(res);
+
+ if (!rp)
+ return EAI_SYSTEM;
+
+ return sockfd;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_network.h b/drivers/staging/usbip/userspace/src/usbip_network.h
new file mode 100644
index 00000000000..c1e875cf107
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_network.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifndef __USBIP_NETWORK_H
+#define __USBIP_NETWORK_H
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+extern int usbip_port;
+extern char *usbip_port_string;
+void usbip_setup_port_number(char *arg);
+
+/* ---------------------------------------------------------------------- */
+/* Common header for all the kinds of PDUs. */
+struct op_common {
+ uint16_t version;
+
+#define OP_REQUEST (0x80 << 8)
+#define OP_REPLY (0x00 << 8)
+ uint16_t code;
+
+ /* add more error code */
+#define ST_OK 0x00
+#define ST_NA 0x01
+ uint32_t status; /* op_code status (for reply) */
+
+} __attribute__((packed));
+
+#define PACK_OP_COMMON(pack, op_common) do {\
+ usbip_net_pack_uint16_t(pack, &(op_common)->version);\
+ usbip_net_pack_uint16_t(pack, &(op_common)->code);\
+ usbip_net_pack_uint32_t(pack, &(op_common)->status);\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Dummy Code */
+#define OP_UNSPEC 0x00
+#define OP_REQ_UNSPEC OP_UNSPEC
+#define OP_REP_UNSPEC OP_UNSPEC
+
+/* ---------------------------------------------------------------------- */
+/* Retrieve USB device information. (still not used) */
+#define OP_DEVINFO 0x02
+#define OP_REQ_DEVINFO (OP_REQUEST | OP_DEVINFO)
+#define OP_REP_DEVINFO (OP_REPLY | OP_DEVINFO)
+
+struct op_devinfo_request {
+ char busid[SYSFS_BUS_ID_SIZE];
+} __attribute__((packed));
+
+struct op_devinfo_reply {
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+/* ---------------------------------------------------------------------- */
+/* Import a remote USB device. */
+#define OP_IMPORT 0x03
+#define OP_REQ_IMPORT (OP_REQUEST | OP_IMPORT)
+#define OP_REP_IMPORT (OP_REPLY | OP_IMPORT)
+
+struct op_import_request {
+ char busid[SYSFS_BUS_ID_SIZE];
+} __attribute__((packed));
+
+struct op_import_reply {
+ struct usbip_usb_device udev;
+// struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+#define PACK_OP_IMPORT_REQUEST(pack, request) do {\
+} while (0)
+
+#define PACK_OP_IMPORT_REPLY(pack, reply) do {\
+ usbip_net_pack_usb_device(pack, &(reply)->udev);\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Export a USB device to a remote host. */
+#define OP_EXPORT 0x06
+#define OP_REQ_EXPORT (OP_REQUEST | OP_EXPORT)
+#define OP_REP_EXPORT (OP_REPLY | OP_EXPORT)
+
+struct op_export_request {
+ struct usbip_usb_device udev;
+} __attribute__((packed));
+
+struct op_export_reply {
+ int returncode;
+} __attribute__((packed));
+
+
+#define PACK_OP_EXPORT_REQUEST(pack, request) do {\
+ usbip_net_pack_usb_device(pack, &(request)->udev);\
+} while (0)
+
+#define PACK_OP_EXPORT_REPLY(pack, reply) do {\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* un-Export a USB device from a remote host. */
+#define OP_UNEXPORT 0x07
+#define OP_REQ_UNEXPORT (OP_REQUEST | OP_UNEXPORT)
+#define OP_REP_UNEXPORT (OP_REPLY | OP_UNEXPORT)
+
+struct op_unexport_request {
+ struct usbip_usb_device udev;
+} __attribute__((packed));
+
+struct op_unexport_reply {
+ int returncode;
+} __attribute__((packed));
+
+#define PACK_OP_UNEXPORT_REQUEST(pack, request) do {\
+ usbip_net_pack_usb_device(pack, &(request)->udev);\
+} while (0)
+
+#define PACK_OP_UNEXPORT_REPLY(pack, reply) do {\
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+/* Negotiate IPSec encryption key. (still not used) */
+#define OP_CRYPKEY 0x04
+#define OP_REQ_CRYPKEY (OP_REQUEST | OP_CRYPKEY)
+#define OP_REP_CRYPKEY (OP_REPLY | OP_CRYPKEY)
+
+struct op_crypkey_request {
+ /* 128bit key */
+ uint32_t key[4];
+} __attribute__((packed));
+
+struct op_crypkey_reply {
+ uint32_t __reserved;
+} __attribute__((packed));
+
+
+/* ---------------------------------------------------------------------- */
+/* Retrieve the list of exported USB devices. */
+#define OP_DEVLIST 0x05
+#define OP_REQ_DEVLIST (OP_REQUEST | OP_DEVLIST)
+#define OP_REP_DEVLIST (OP_REPLY | OP_DEVLIST)
+
+struct op_devlist_request {
+} __attribute__((packed));
+
+struct op_devlist_reply {
+ uint32_t ndev;
+ /* followed by reply_extra[] */
+} __attribute__((packed));
+
+struct op_devlist_reply_extra {
+ struct usbip_usb_device udev;
+ struct usbip_usb_interface uinf[];
+} __attribute__((packed));
+
+#define PACK_OP_DEVLIST_REQUEST(pack, request) do {\
+} while (0)
+
+#define PACK_OP_DEVLIST_REPLY(pack, reply) do {\
+ usbip_net_pack_uint32_t(pack, &(reply)->ndev);\
+} while (0)
+
+void usbip_net_pack_uint32_t(int pack, uint32_t *num);
+void usbip_net_pack_uint16_t(int pack, uint16_t *num);
+void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
+void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
+
+ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
+ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
+int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
+int usbip_net_recv_op_common(int sockfd, uint16_t *code);
+int usbip_net_set_reuseaddr(int sockfd);
+int usbip_net_set_nodelay(int sockfd);
+int usbip_net_set_keepalive(int sockfd);
+int usbip_net_set_v6only(int sockfd);
+int usbip_net_tcp_connect(char *hostname, char *port);
+
+#endif /* __USBIP_NETWORK_H */
diff --git a/drivers/staging/usbip/userspace/src/usbip_port.c b/drivers/staging/usbip/userspace/src/usbip_port.c
new file mode 100644
index 00000000000..a2e884fd922
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_port.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "vhci_driver.h"
+#include "usbip_common.h"
+
+static int list_imported_devices(void)
+{
+ int i;
+ struct usbip_imported_device *idev;
+ int ret;
+
+ ret = usbip_vhci_driver_open();
+ if (ret < 0) {
+ err("open vhci_driver");
+ return -1;
+ }
+
+ printf("Imported USB devices\n");
+ printf("====================\n");
+
+ for (i = 0; i < vhci_driver->nports; i++) {
+ idev = &vhci_driver->idev[i];
+
+ if (usbip_vhci_imported_device_dump(idev) < 0)
+ ret = -1;
+ }
+
+ usbip_vhci_driver_close();
+
+ return ret;
+
+}
+
+int usbip_port_show(__attribute__((unused)) int argc,
+ __attribute__((unused)) char *argv[])
+{
+ int ret;
+
+ ret = list_imported_devices();
+ if (ret < 0)
+ err("list imported devices");
+
+ return ret;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbip_unbind.c b/drivers/staging/usbip/userspace/src/usbip_unbind.c
new file mode 100644
index 00000000000..a4a496c9cba
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbip_unbind.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libudev.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <getopt.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "usbip.h"
+#include "sysfs_utils.h"
+
+static const char usbip_unbind_usage_string[] =
+ "usbip unbind <args>\n"
+ " -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from "
+ "device on <busid>\n";
+
+void usbip_unbind_usage(void)
+{
+ printf("usage: %s", usbip_unbind_usage_string);
+}
+
+static int unbind_device(char *busid)
+{
+ char bus_type[] = "usb";
+ int rc, ret = -1;
+
+ char unbind_attr_name[] = "unbind";
+ char unbind_attr_path[SYSFS_PATH_MAX];
+ char rebind_attr_name[] = "rebind";
+ char rebind_attr_path[SYSFS_PATH_MAX];
+
+ struct udev *udev;
+ struct udev_device *dev;
+ const char *driver;
+
+ /* Create libudev context. */
+ udev = udev_new();
+
+ /* Check whether the device with this bus ID exists. */
+ dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid);
+ if (!dev) {
+ err("device with the specified bus ID does not exist");
+ goto err_close_udev;
+ }
+
+ /* Check whether the device is using usbip-host driver. */
+ driver = udev_device_get_driver(dev);
+ if (!driver || strcmp(driver, "usbip-host")) {
+ err("device is not bound to usbip-host driver");
+ goto err_close_udev;
+ }
+
+ /* Unbind device from driver. */
+ snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
+ USBIP_HOST_DRV_NAME, unbind_attr_name);
+
+ rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error unbinding device %s from driver", busid);
+ goto err_close_udev;
+ }
+
+ /* Notify driver of unbind. */
+ rc = modify_match_busid(busid, 0);
+ if (rc < 0) {
+ err("unable to unbind device on %s", busid);
+ goto err_close_udev;
+ }
+
+ /* Trigger new probing. */
+ snprintf(rebind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s",
+ SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
+ USBIP_HOST_DRV_NAME, rebind_attr_name);
+
+ rc = write_sysfs_attribute(rebind_attr_path, busid, strlen(busid));
+ if (rc < 0) {
+ err("error rebinding");
+ goto err_close_udev;
+ }
+
+ ret = 0;
+ info("unbind device on busid %s: complete", busid);
+
+err_close_udev:
+ udev_device_unref(dev);
+ udev_unref(udev);
+
+ return ret;
+}
+
+int usbip_unbind(int argc, char *argv[])
+{
+ static const struct option opts[] = {
+ { "busid", required_argument, NULL, 'b' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+ int ret = -1;
+
+ for (;;) {
+ opt = getopt_long(argc, argv, "b:", opts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'b':
+ ret = unbind_device(optarg);
+ goto out;
+ default:
+ goto err_out;
+ }
+ }
+
+err_out:
+ usbip_unbind_usage();
+out:
+ return ret;
+}
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
new file mode 100644
index 00000000000..2f87f2d348b
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include <getopt.h>
+#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"
+#define MAXSOCKFD 20
+
+#define MAIN_LOOP_TIMEOUT 10
+
+#define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid"
+
+static const char usbip_version_string[] = PACKAGE_STRING;
+
+static const char usbipd_help_string[] =
+ "usage: usbipd [options]\n"
+ "\n"
+ " -4, --ipv4\n"
+ " Bind to IPv4. Default is both.\n"
+ "\n"
+ " -6, --ipv6\n"
+ " Bind to IPv6. Default is both.\n"
+ "\n"
+ " -D, --daemon\n"
+ " Run as a daemon process.\n"
+ "\n"
+ " -d, --debug\n"
+ " Print debugging information.\n"
+ "\n"
+ " -PFILE, --pid FILE\n"
+ " Write process id to FILE.\n"
+ " If no FILE specified, use " DEFAULT_PID_FILE "\n"
+ "\n"
+ " -tPORT, --tcp-port PORT\n"
+ " Listen on TCP/IP port PORT.\n"
+ "\n"
+ " -h, --help\n"
+ " Print this help.\n"
+ "\n"
+ " -v, --version\n"
+ " Show version.\n";
+
+static void usbipd_help(void)
+{
+ printf("%s\n", usbipd_help_string);
+}
+
+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;
+}
+
+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;
+}
+
+#ifdef HAVE_LIBWRAP
+static int tcpd_auth(int connfd)
+{
+ struct request_info request;
+ int rc;
+
+ request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
+ fromhost(&request);
+ rc = hosts_access(&request);
+ if (rc == 0)
+ return -1;
+
+ return 0;
+}
+#endif
+
+static int do_accept(int listenfd)
+{
+ 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));
+
+ connfd = accept(listenfd, (struct sockaddr *)&ss, &len);
+ if (connfd < 0) {
+ err("failed to accept connection");
+ return -1;
+ }
+
+ rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host),
+ port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc)
+ err("getnameinfo: %s", gai_strerror(rc));
+
+#ifdef HAVE_LIBWRAP
+ rc = tcpd_auth(connfd);
+ if (rc < 0) {
+ info("denied access from %s", host);
+ close(connfd);
+ return -1;
+ }
+#endif
+ info("connection from %s:%s", host, port);
+
+ return connfd;
+}
+
+int process_request(int listenfd)
+{
+ pid_t childpid;
+ int connfd;
+
+ connfd = do_accept(listenfd);
+ if (connfd < 0)
+ return -1;
+ childpid = fork();
+ if (childpid == 0) {
+ close(listenfd);
+ recv_pdu(connfd);
+ exit(0);
+ }
+ close(connfd);
+ return 0;
+}
+
+static void addrinfo_to_text(struct addrinfo *ai, char buf[],
+ const size_t buf_size)
+{
+ char hbuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+ int rc;
+
+ buf[0] = '\0';
+
+ rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc)
+ err("getnameinfo: %s", gai_strerror(rc));
+
+ snprintf(buf, buf_size, "%s:%s", hbuf, sbuf);
+}
+
+static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[],
+ int maxsockfd)
+{
+ struct addrinfo *ai;
+ int ret, nsockfd = 0;
+ const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2;
+ char ai_buf[ai_buf_size];
+
+ for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) {
+ int sock;
+
+ addrinfo_to_text(ai, ai_buf, ai_buf_size);
+ dbg("opening %s", ai_buf);
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0) {
+ err("socket: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ continue;
+ }
+
+ usbip_net_set_reuseaddr(sock);
+ usbip_net_set_nodelay(sock);
+ /* We use seperate sockets for IPv4 and IPv6
+ * (see do_standalone_mode()) */
+ usbip_net_set_v6only(sock);
+
+ if (sock >= FD_SETSIZE) {
+ err("FD_SETSIZE: %s: sock=%d, max=%d",
+ ai_buf, sock, FD_SETSIZE);
+ close(sock);
+ continue;
+ }
+
+ ret = bind(sock, ai->ai_addr, ai->ai_addrlen);
+ if (ret < 0) {
+ err("bind: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ ret = listen(sock, SOMAXCONN);
+ if (ret < 0) {
+ err("listen: %s: %d (%s)",
+ ai_buf, errno, strerror(errno));
+ close(sock);
+ continue;
+ }
+
+ info("listening on %s", ai_buf);
+ sockfdlist[nsockfd++] = sock;
+ }
+
+ return nsockfd;
+}
+
+static struct addrinfo *do_getaddrinfo(char *host, int ai_family)
+{
+ struct addrinfo hints, *ai_head;
+ int rc;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head);
+ if (rc) {
+ err("failed to get a network address %s: %s", usbip_port_string,
+ gai_strerror(rc));
+ return NULL;
+ }
+
+ return ai_head;
+}
+
+static void signal_handler(int i)
+{
+ dbg("received '%s' signal", strsignal(i));
+}
+
+static void set_signal(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGCLD, &act, NULL);
+}
+
+static const char *pid_file;
+
+static void write_pid_file(void)
+{
+ if (pid_file) {
+ dbg("creating pid file %s", pid_file);
+ FILE *fp;
+
+ fp = fopen(pid_file, "w");
+ if (!fp) {
+ err("pid_file: %s: %d (%s)",
+ pid_file, errno, strerror(errno));
+ return;
+ }
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+static void remove_pid_file(void)
+{
+ if (pid_file) {
+ dbg("removing pid file %s", pid_file);
+ unlink(pid_file);
+ }
+}
+
+static int do_standalone_mode(int daemonize, int ipv4, int ipv6)
+{
+ struct addrinfo *ai_head;
+ int sockfdlist[MAXSOCKFD];
+ int nsockfd, family;
+ int i, terminate;
+ struct pollfd *fds;
+ struct timespec timeout;
+ sigset_t sigmask;
+
+ if (usbip_host_driver_open()) {
+ err("please load " USBIP_CORE_MOD_NAME ".ko and "
+ USBIP_HOST_DRV_NAME ".ko!");
+ return -1;
+ }
+
+ if (daemonize) {
+ if (daemon(0, 0) < 0) {
+ err("daemonizing failed: %s", strerror(errno));
+ usbip_host_driver_close();
+ return -1;
+ }
+ umask(0);
+ usbip_use_syslog = 1;
+ }
+ set_signal();
+ write_pid_file();
+
+ info("starting " PROGNAME " (%s)", usbip_version_string);
+
+ /*
+ * To suppress warnings on systems with bindv6only disabled
+ * (default), we use seperate sockets for IPv6 and IPv4 and set
+ * IPV6_V6ONLY on the IPv6 sockets.
+ */
+ if (ipv4 && ipv6)
+ family = AF_UNSPEC;
+ else if (ipv4)
+ family = AF_INET;
+ else
+ family = AF_INET6;
+
+ ai_head = do_getaddrinfo(NULL, family);
+ if (!ai_head) {
+ usbip_host_driver_close();
+ return -1;
+ }
+ nsockfd = listen_all_addrinfo(ai_head, sockfdlist,
+ sizeof(sockfdlist) / sizeof(*sockfdlist));
+ freeaddrinfo(ai_head);
+ if (nsockfd <= 0) {
+ err("failed to open a listening socket");
+ usbip_host_driver_close();
+ return -1;
+ }
+
+ dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
+
+ fds = calloc(nsockfd, sizeof(struct pollfd));
+ for (i = 0; i < nsockfd; i++) {
+ fds[i].fd = sockfdlist[i];
+ fds[i].events = POLLIN;
+ }
+ timeout.tv_sec = MAIN_LOOP_TIMEOUT;
+ timeout.tv_nsec = 0;
+
+ sigfillset(&sigmask);
+ sigdelset(&sigmask, SIGTERM);
+ sigdelset(&sigmask, SIGINT);
+
+ terminate = 0;
+ while (!terminate) {
+ int r;
+
+ r = ppoll(fds, nsockfd, &timeout, &sigmask);
+ if (r < 0) {
+ dbg("%s", strerror(errno));
+ terminate = 1;
+ } else if (r) {
+ for (i = 0; i < nsockfd; i++) {
+ if (fds[i].revents & POLLIN) {
+ dbg("read event on fd[%d]=%d",
+ i, sockfdlist[i]);
+ process_request(sockfdlist[i]);
+ }
+ }
+ } else {
+ dbg("heartbeat timeout on ppoll()");
+ }
+ }
+
+ info("shutting down " PROGNAME);
+ free(fds);
+ usbip_host_driver_close();
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "ipv4", no_argument, NULL, '4' },
+ { "ipv6", no_argument, NULL, '6' },
+ { "daemon", no_argument, NULL, 'D' },
+ { "daemon", no_argument, NULL, 'D' },
+ { "debug", no_argument, NULL, 'd' },
+ { "pid", optional_argument, NULL, 'P' },
+ { "tcp-port", required_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ enum {
+ cmd_standalone_mode = 1,
+ cmd_help,
+ cmd_version
+ } cmd;
+
+ int daemonize = 0;
+ int ipv4 = 0, ipv6 = 0;
+ int opt, rc = -1;
+
+ pid_file = NULL;
+
+ usbip_use_stderr = 1;
+ usbip_use_syslog = 0;
+
+ if (geteuid() != 0)
+ err("not running as root?");
+
+ cmd = cmd_standalone_mode;
+ for (;;) {
+ opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL);
+
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case '4':
+ ipv4 = 1;
+ break;
+ case '6':
+ ipv6 = 1;
+ break;
+ case 'D':
+ daemonize = 1;
+ break;
+ case 'd':
+ usbip_use_debug = 1;
+ break;
+ case 'h':
+ cmd = cmd_help;
+ break;
+ case 'P':
+ pid_file = optarg ? optarg : DEFAULT_PID_FILE;
+ break;
+ case 't':
+ usbip_setup_port_number(optarg);
+ break;
+ case 'v':
+ cmd = cmd_version;
+ break;
+ case '?':
+ usbipd_help();
+ default:
+ goto err_out;
+ }
+ }
+
+ if (!ipv4 && !ipv6)
+ ipv4 = ipv6 = 1;
+
+ switch (cmd) {
+ case cmd_standalone_mode:
+ rc = do_standalone_mode(daemonize, ipv4, ipv6);
+ remove_pid_file();
+ break;
+ case cmd_version:
+ printf(PROGNAME " (%s)\n", usbip_version_string);
+ rc = 0;
+ break;
+ case cmd_help:
+ usbipd_help();
+ rc = 0;
+ break;
+ default:
+ usbipd_help();
+ goto err_out;
+ }
+
+err_out:
+ return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/drivers/staging/usbip/userspace/src/utils.c b/drivers/staging/usbip/userspace/src/utils.c
new file mode 100644
index 00000000000..2b3d6d23501
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/utils.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "usbip_common.h"
+#include "utils.h"
+#include "sysfs_utils.h"
+
+int modify_match_busid(char *busid, int add)
+{
+ char attr_name[] = "match_busid";
+ char command[SYSFS_BUS_ID_SIZE + 4];
+ char match_busid_attr_path[SYSFS_PATH_MAX];
+ int rc;
+
+ snprintf(match_busid_attr_path, sizeof(match_busid_attr_path),
+ "%s/%s/%s/%s/%s/%s", SYSFS_MNT_PATH, SYSFS_BUS_NAME,
+ SYSFS_BUS_TYPE, SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME,
+ attr_name);
+
+ if (add)
+ snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s", busid);
+ else
+ snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s", busid);
+
+ rc = write_sysfs_attribute(match_busid_attr_path, command,
+ sizeof(command));
+ if (rc < 0) {
+ dbg("failed to write match_busid: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/usbip/userspace/src/utils.h b/drivers/staging/usbip/userspace/src/utils.h
new file mode 100644
index 00000000000..5916fd3e02a
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/utils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
+ * 2005-2007 Takahiro Hirofuchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __UTILS_H
+#define __UTILS_H
+
+int modify_match_busid(char *busid, int add);
+
+#endif /* __UTILS_H */
+
diff --git a/drivers/staging/usbip/vhci.h b/drivers/staging/usbip/vhci.h
index 41a1fe5138f..a863a98a91c 100644
--- a/drivers/staging/usbip/vhci.h
+++ b/drivers/staging/usbip/vhci.h
@@ -6,20 +6,19 @@
* 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.
*/
-#include <linux/platform_device.h>
-#include <linux/usb/hcd.h>
+#ifndef __USBIP_VHCI_H
+#define __USBIP_VHCI_H
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/wait.h>
struct vhci_device {
struct usb_device *udev;
@@ -33,12 +32,11 @@ struct vhci_device {
/* speed of a remote device */
enum usb_device_speed speed;
- /* vhci root-hub port to which this device is attached */
+ /* vhci root-hub port to which this device is attached */
__u32 rhport;
struct usbip_device ud;
-
/* lock for the below link lists */
spinlock_t priv_lock;
@@ -54,7 +52,6 @@ struct vhci_device {
wait_queue_head_t waitq_tx;
};
-
/* urb->hcpriv, use container_of() */
struct vhci_priv {
unsigned long seqnum;
@@ -64,7 +61,6 @@ struct vhci_priv {
struct urb *urb;
};
-
struct vhci_unlink {
/* seqnum of this request */
unsigned long seqnum;
@@ -75,22 +71,17 @@ struct vhci_unlink {
unsigned long unlink_seqnum;
};
-/*
- * The number of ports is less than 16 ?
- * USB_MAXCHILDREN is statically defined to 16 in usb.h. Its maximum value
- * would be 31 because the event_bits[1] of struct usb_hub is defined as
- * unsigned long in hub.h
- */
+/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
#define VHCI_NPORTS 8
/* for usb_bus.hcpriv */
struct vhci_hcd {
- spinlock_t lock;
+ spinlock_t lock;
- u32 port_status[VHCI_NPORTS];
+ u32 port_status[VHCI_NPORTS];
- unsigned resuming:1;
- unsigned long re_timeout;
+ unsigned resuming:1;
+ unsigned long re_timeout;
atomic_t seqnum;
@@ -100,26 +91,20 @@ struct vhci_hcd {
* But, the index of this array begins from 0.
*/
struct vhci_device vdev[VHCI_NPORTS];
-
- /* vhci_device which has not been assiged its address yet */
- int pending_port;
};
-
extern struct vhci_hcd *the_controller;
-extern struct attribute_group dev_attr_group;
-
-
-/*-------------------------------------------------------------------------*/
-/* prototype declaration */
+extern const struct attribute_group dev_attr_group;
/* vhci_hcd.c */
void rh_port_connect(int rhport, enum usb_device_speed speed);
-void rh_port_disconnect(int rhport);
-void vhci_rx_loop(struct usbip_task *ut);
-void vhci_tx_loop(struct usbip_task *ut);
-#define hardware (&the_controller->pdev.dev)
+/* vhci_rx.c */
+struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum);
+int vhci_rx_loop(void *data);
+
+/* vhci_tx.c */
+int vhci_tx_loop(void *data);
static inline struct vhci_device *port_to_vdev(__u32 port)
{
@@ -140,3 +125,5 @@ static inline struct device *vhci_dev(struct vhci_hcd *vhci)
{
return vhci_to_hcd(vhci)->self.controller;
}
+
+#endif /* __USBIP_VHCI_H */
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index 832608d3e57..0007d30e45b 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -17,20 +17,19 @@
* USA.
*/
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "usbip_common.h"
#include "vhci.h"
-#define DRIVER_VERSION "1.0"
#define DRIVER_AUTHOR "Takahiro Hirofuchi"
-#define DRIVER_DESC "Virtual Host Controller Interface Driver for USB/IP"
-#define DRIVER_LICENCE "GPL"
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENCE);
-
-
+#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver"
/*
* TODO
@@ -42,15 +41,13 @@ MODULE_LICENSE(DRIVER_LICENCE);
* - clean up everything
*/
-
/* See usb gadget dummy hcd */
-
static int vhci_hub_status(struct usb_hcd *hcd, char *buff);
static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
- u16 wIndex, char *buff, u16 wLength);
+ u16 wIndex, char *buff, u16 wLength);
static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
- gfp_t mem_flags);
+ gfp_t mem_flags);
static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
static int vhci_start(struct usb_hcd *vhci_hcd);
static void vhci_stop(struct usb_hcd *hcd);
@@ -61,64 +58,72 @@ static const char driver_desc[] = "USB/IP Virtual Host Controller";
struct vhci_hcd *the_controller;
-static const char *bit_desc[] = {
+static const char * const bit_desc[] = {
"CONNECTION", /*0*/
"ENABLE", /*1*/
"SUSPEND", /*2*/
"OVER_CURRENT", /*3*/
"RESET", /*4*/
- "R5", /*5*/
- "R6", /*6*/
- "R7", /*7*/
+ "R5", /*5*/
+ "R6", /*6*/
+ "R7", /*7*/
"POWER", /*8*/
"LOWSPEED", /*9*/
"HIGHSPEED", /*10*/
"PORT_TEST", /*11*/
"INDICATOR", /*12*/
- "R13", /*13*/
- "R14", /*14*/
- "R15", /*15*/
+ "R13", /*13*/
+ "R14", /*14*/
+ "R15", /*15*/
"C_CONNECTION", /*16*/
"C_ENABLE", /*17*/
"C_SUSPEND", /*18*/
"C_OVER_CURRENT", /*19*/
"C_RESET", /*20*/
- "R21", /*21*/
- "R22", /*22*/
- "R23", /*23*/
- "R24", /*24*/
- "R25", /*25*/
- "R26", /*26*/
- "R27", /*27*/
- "R28", /*28*/
- "R29", /*29*/
- "R30", /*30*/
- "R31", /*31*/
+ "R21", /*21*/
+ "R22", /*22*/
+ "R23", /*23*/
+ "R24", /*24*/
+ "R25", /*25*/
+ "R26", /*26*/
+ "R27", /*27*/
+ "R28", /*28*/
+ "R29", /*29*/
+ "R30", /*30*/
+ "R31", /*31*/
};
-
-static void dump_port_status(u32 status)
+static void dump_port_status_diff(u32 prev_status, u32 new_status)
{
int i = 0;
-
- printk(KERN_DEBUG "status %08x:", status);
- for (i = 0; i < 32; i++) {
- if (status & (1 << i))
- printk(" %s", bit_desc[i]);
+ u32 bit = 1;
+
+ pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status);
+ while (bit) {
+ u32 prev = prev_status & bit;
+ u32 new = new_status & bit;
+ char change;
+
+ if (!prev && new)
+ change = '+';
+ else if (prev && !new)
+ change = '-';
+ else
+ change = ' ';
+
+ if (prev || new)
+ pr_debug(" %c%s\n", change, bit_desc[i]);
+ bit <<= 1;
+ i++;
}
-
- printk("\n");
+ pr_debug("\n");
}
-
-
void rh_port_connect(int rhport, enum usb_device_speed speed)
{
- unsigned long flags;
-
usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock(&the_controller->lock);
the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION
| (1 << USB_PORT_FEAT_C_CONNECTION);
@@ -134,91 +139,58 @@ void rh_port_connect(int rhport, enum usb_device_speed speed)
break;
}
- /* spin_lock(&the_controller->vdev[rhport].ud.lock);
- * the_controller->vdev[rhport].ud.status = VDEV_CONNECT;
- * spin_unlock(&the_controller->vdev[rhport].ud.lock); */
-
- the_controller->pending_port = rhport;
-
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&the_controller->lock);
usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
}
-void rh_port_disconnect(int rhport)
+static void rh_port_disconnect(int rhport)
{
- unsigned long flags;
-
usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport);
- spin_lock_irqsave(&the_controller->lock, flags);
- /* stop_activity(dum, driver); */
+ spin_lock(&the_controller->lock);
+
the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION;
the_controller->port_status[rhport] |=
(1 << USB_PORT_FEAT_C_CONNECTION);
-
- /* not yet complete the disconnection
- * spin_lock(&vdev->ud.lock);
- * vdev->ud.status = VHC_ST_DISCONNECT;
- * spin_unlock(&vdev->ud.lock); */
-
- spin_unlock_irqrestore(&the_controller->lock, flags);
-
+ spin_unlock(&the_controller->lock);
usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
}
-
-
-/*----------------------------------------------------------------------*/
-
-#define PORT_C_MASK \
- ((USB_PORT_STAT_C_CONNECTION \
- | USB_PORT_STAT_C_ENABLE \
- | USB_PORT_STAT_C_SUSPEND \
- | USB_PORT_STAT_C_OVERCURRENT \
+#define PORT_C_MASK \
+ ((USB_PORT_STAT_C_CONNECTION \
+ | USB_PORT_STAT_C_ENABLE \
+ | USB_PORT_STAT_C_SUSPEND \
+ | USB_PORT_STAT_C_OVERCURRENT \
| USB_PORT_STAT_C_RESET) << 16)
/*
- * This function is almostly the same as dummy_hcd.c:dummy_hub_status() without
- * suspend/resume support. But, it is modified to provide multiple ports.
+ * Returns 0 if the status hasn't changed, or the number of bytes in buf.
+ * Ports are 0-indexed from the HCD point of view,
+ * and 1-indexed from the USB core pointer of view.
*
* @buf: a bitmap to show which port status has been changed.
- * bit 0: reserved or used for another purpose?
+ * bit 0: reserved
* bit 1: the status of port 0 has been changed.
* bit 2: the status of port 1 has been changed.
* ...
- * bit 7: the status of port 6 has been changed.
- * bit 8: the status of port 7 has been changed.
- * ...
- * bit 15: the status of port 14 has been changed.
- *
- * So, the maximum number of ports is 31 ( port 0 to port 30) ?
- *
- * The return value is the actual transfered length in byte. If nothing has
- * been changed, return 0. In the case that the number of ports is less than or
- * equal to 6 (VHCI_NPORTS==7), return 1.
- *
*/
static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
{
struct vhci_hcd *vhci;
- unsigned long flags;
- int retval = 0;
-
- /* the enough buffer is allocated according to USB_MAXCHILDREN */
- unsigned long *event_bits = (unsigned long *) buf;
+ int retval;
int rhport;
int changed = 0;
-
- *event_bits = 0;
+ retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8);
+ memset(buf, 0, retval);
vhci = hcd_to_vhci(hcd);
- spin_lock_irqsave(&vhci->lock, flags);
+ spin_lock(&vhci->lock);
if (!HCD_HW_ACCESSIBLE(hcd)) {
- usbip_dbg_vhci_rh("hw accessible flag in on?\n");
+ usbip_dbg_vhci_rh("hw accessible flag not on?\n");
goto done;
}
@@ -226,39 +198,30 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
if ((vhci->port_status[rhport] & PORT_C_MASK)) {
/* The status of a port has been changed, */
- usbip_dbg_vhci_rh("port %d is changed\n", rhport);
+ usbip_dbg_vhci_rh("port %d status changed\n", rhport);
- *event_bits |= 1 << (rhport + 1);
+ buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8;
changed = 1;
}
}
- usbip_uinfo("changed %d\n", changed);
-
- if (hcd->state == HC_STATE_SUSPENDED)
+ if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
usb_hcd_resume_root_hub(hcd);
- if (changed)
- retval = 1 + (VHCI_NPORTS / 8);
- else
- retval = 0;
-
done:
- spin_unlock_irqrestore(&vhci->lock, flags);
- return retval;
+ spin_unlock(&vhci->lock);
+ return changed ? retval : 0;
}
-/* See hub_configure in hub.c */
static inline void hub_descriptor(struct usb_hub_descriptor *desc)
{
memset(desc, 0, sizeof(*desc));
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
- desc->wHubCharacteristics = (__force __u16)
- (__constant_cpu_to_le16(0x0001));
+ desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001));
desc->bNbrPorts = VHCI_NPORTS;
- desc->bitmap[0] = 0xff;
- desc->bitmap[1] = 0xff;
+ desc->u.hs.DeviceRemovable[0] = 0xff;
+ desc->u.hs.DeviceRemovable[1] = 0xff;
}
static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
@@ -266,7 +229,6 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
{
struct vhci_hcd *dum;
int retval = 0;
- unsigned long flags;
int rhport;
u32 prev_port_status[VHCI_NPORTS];
@@ -279,21 +241,19 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* wIndex shows the port number and begins from 1.
*/
usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
- wIndex);
+ wIndex);
if (wIndex > VHCI_NPORTS)
- printk(KERN_ERR "%s: invalid port number %d\n", __func__,
- wIndex);
+ pr_err("invalid port number %d\n", wIndex);
rhport = ((__u8)(wIndex & 0x00ff)) - 1;
dum = hcd_to_vhci(hcd);
- spin_lock_irqsave(&dum->lock, flags);
+ spin_lock(&dum->lock);
/* store old status and compare now and old later */
if (usbip_dbg_flag_vhci_rh) {
- int i = 0;
- for (i = 0; i < VHCI_NPORTS; i++)
- prev_port_status[i] = dum->port_status[i];
+ memcpy(prev_port_status, dum->port_status,
+ sizeof(prev_port_status));
}
switch (typeReq) {
@@ -311,32 +271,31 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
break;
case USB_PORT_FEAT_POWER:
- usbip_dbg_vhci_rh(" ClearPortFeature: "
- "USB_PORT_FEAT_POWER\n");
+ usbip_dbg_vhci_rh(
+ " ClearPortFeature: USB_PORT_FEAT_POWER\n");
dum->port_status[rhport] = 0;
- /* dum->address = 0; */
- /* dum->hdev = 0; */
dum->resuming = 0;
break;
case USB_PORT_FEAT_C_RESET:
- usbip_dbg_vhci_rh(" ClearPortFeature: "
- "USB_PORT_FEAT_C_RESET\n");
+ usbip_dbg_vhci_rh(
+ " ClearPortFeature: USB_PORT_FEAT_C_RESET\n");
switch (dum->vdev[rhport].speed) {
case USB_SPEED_HIGH:
dum->port_status[rhport] |=
- USB_PORT_STAT_HIGH_SPEED;
+ USB_PORT_STAT_HIGH_SPEED;
break;
case USB_SPEED_LOW:
dum->port_status[rhport] |=
- USB_PORT_STAT_LOW_SPEED;
+ USB_PORT_STAT_LOW_SPEED;
break;
default:
break;
}
default:
usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n",
- wValue);
+ wValue);
dum->port_status[rhport] &= ~(1 << wValue);
+ break;
}
break;
case GetHubDescriptor:
@@ -350,80 +309,48 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case GetPortStatus:
usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
if (wIndex > VHCI_NPORTS || wIndex < 1) {
- printk(KERN_ERR "%s: invalid port number %d\n",
- __func__, wIndex);
+ pr_err("invalid port number %d\n", wIndex);
retval = -EPIPE;
}
- /* we do no care of resume. */
+ /* we do not care about resume. */
/* whoever resets or resumes must GetPortStatus to
* complete it!!
- * */
+ */
if (dum->resuming && time_after(jiffies, dum->re_timeout)) {
- printk(KERN_ERR "%s: not yet\n", __func__);
dum->port_status[rhport] |=
- (1 << USB_PORT_FEAT_C_SUSPEND);
+ (1 << USB_PORT_FEAT_C_SUSPEND);
dum->port_status[rhport] &=
- ~(1 << USB_PORT_FEAT_SUSPEND);
+ ~(1 << USB_PORT_FEAT_SUSPEND);
dum->resuming = 0;
dum->re_timeout = 0;
- /* if (dum->driver && dum->driver->resume) {
- * spin_unlock (&dum->lock);
- * dum->driver->resume (&dum->gadget);
- * spin_lock (&dum->lock);
- * } */
}
if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) !=
- 0 && time_after(jiffies, dum->re_timeout)) {
+ 0 && time_after(jiffies, dum->re_timeout)) {
dum->port_status[rhport] |=
- (1 << USB_PORT_FEAT_C_RESET);
+ (1 << USB_PORT_FEAT_C_RESET);
dum->port_status[rhport] &=
- ~(1 << USB_PORT_FEAT_RESET);
+ ~(1 << USB_PORT_FEAT_RESET);
dum->re_timeout = 0;
if (dum->vdev[rhport].ud.status ==
- VDEV_ST_NOTASSIGNED) {
- usbip_dbg_vhci_rh(" enable rhport %d "
- "(status %u)\n",
- rhport,
- dum->vdev[rhport].ud.status);
+ VDEV_ST_NOTASSIGNED) {
+ usbip_dbg_vhci_rh(
+ " enable rhport %d (status %u)\n",
+ rhport,
+ dum->vdev[rhport].ud.status);
dum->port_status[rhport] |=
- USB_PORT_STAT_ENABLE;
+ USB_PORT_STAT_ENABLE;
}
-#if 0
- if (dum->driver) {
-
- dum->port_status[rhport] |=
- USB_PORT_STAT_ENABLE;
- /* give it the best speed we agree on */
- dum->gadget.speed = dum->driver->speed;
- dum->gadget.ep0->maxpacket = 64;
- switch (dum->gadget.speed) {
- case USB_SPEED_HIGH:
- dum->port_status[rhport] |=
- USB_PORT_STAT_HIGH_SPEED;
- break;
- case USB_SPEED_LOW:
- dum->gadget.ep0->maxpacket = 8;
- dum->port_status[rhport] |=
- USB_PORT_STAT_LOW_SPEED;
- break;
- default:
- dum->gadget.speed = USB_SPEED_FULL;
- break;
- }
- }
-#endif
-
}
- ((u16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]);
- ((u16 *) buf)[1] =
- cpu_to_le16(dum->port_status[rhport] >> 16);
+ ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]);
+ ((__le16 *) buf)[1] =
+ cpu_to_le16(dum->port_status[rhport] >> 16);
usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0],
- ((u16 *)buf)[1]);
+ ((u16 *)buf)[1]);
break;
case SetHubFeature:
usbip_dbg_vhci_rh(" SetHubFeature\n");
@@ -432,35 +359,18 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- usbip_dbg_vhci_rh(" SetPortFeature: "
- "USB_PORT_FEAT_SUSPEND\n");
- printk(KERN_ERR "%s: not yet\n", __func__);
-#if 0
- dum->port_status[rhport] |=
- (1 << USB_PORT_FEAT_SUSPEND);
- if (dum->driver->suspend) {
- spin_unlock(&dum->lock);
- dum->driver->suspend(&dum->gadget);
- spin_lock(&dum->lock);
- }
-#endif
+ usbip_dbg_vhci_rh(
+ " SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
break;
case USB_PORT_FEAT_RESET:
- usbip_dbg_vhci_rh(" SetPortFeature: "
- "USB_PORT_FEAT_RESET\n");
+ usbip_dbg_vhci_rh(
+ " SetPortFeature: USB_PORT_FEAT_RESET\n");
/* if it's already running, disconnect first */
if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) {
dum->port_status[rhport] &=
- ~(USB_PORT_STAT_ENABLE |
- USB_PORT_STAT_LOW_SPEED |
- USB_PORT_STAT_HIGH_SPEED);
-#if 0
- if (dum->driver) {
- dev_dbg(hardware, "disconnect\n");
- stop_activity(dum, dum->driver);
- }
-#endif
-
+ ~(USB_PORT_STAT_ENABLE |
+ USB_PORT_STAT_LOW_SPEED |
+ USB_PORT_STAT_HIGH_SPEED);
/* FIXME test that code path! */
}
/* 50msec reset signaling */
@@ -469,37 +379,34 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* FALLTHROUGH */
default:
usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
- wValue);
+ wValue);
dum->port_status[rhport] |= (1 << wValue);
+ break;
}
break;
default:
- printk(KERN_ERR "%s: default: no such request\n", __func__);
- /* dev_dbg (hardware,
- * "hub control req%04x v%04x i%04x l%d\n",
- * typeReq, wValue, wIndex, wLength); */
+ pr_err("default: no such request\n");
/* "protocol stall" on error */
retval = -EPIPE;
}
if (usbip_dbg_flag_vhci_rh) {
- printk(KERN_DEBUG "port %d\n", rhport);
- dump_port_status(prev_port_status[rhport]);
- dump_port_status(dum->port_status[rhport]);
+ pr_debug("port %d\n", rhport);
+ /* Only dump valid port status */
+ if (rhport >= 0) {
+ dump_port_status_diff(prev_port_status[rhport],
+ dum->port_status[rhport]);
+ }
}
usbip_dbg_vhci_rh(" bye\n");
- spin_unlock_irqrestore(&dum->lock, flags);
+ spin_unlock(&dum->lock);
return retval;
}
-
-
-/*----------------------------------------------------------------------*/
-
static struct vhci_device *get_vdev(struct usb_device *udev)
{
int i;
@@ -518,39 +425,33 @@ static void vhci_tx_urb(struct urb *urb)
{
struct vhci_device *vdev = get_vdev(urb->dev);
struct vhci_priv *priv;
- unsigned long flag;
if (!vdev) {
- err("could not get virtual device");
- /* BUG(); */
+ pr_err("could not get virtual device");
return;
}
priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC);
-
- spin_lock_irqsave(&vdev->priv_lock, flag);
-
if (!priv) {
- dev_err(&urb->dev->dev, "malloc vhci_priv\n");
- spin_unlock_irqrestore(&vdev->priv_lock, flag);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
return;
}
+ spin_lock(&vdev->priv_lock);
+
priv->seqnum = atomic_inc_return(&the_controller->seqnum);
if (priv->seqnum == 0xffff)
- usbip_uinfo("seqnum max\n");
+ dev_info(&urb->dev->dev, "seqnum max\n");
priv->vdev = vdev;
priv->urb = urb;
urb->hcpriv = (void *) priv;
-
list_add_tail(&priv->list, &vdev->priv_tx);
wake_up(&vdev->waitq_tx);
- spin_unlock_irqrestore(&vdev->priv_lock, flag);
+ spin_unlock(&vdev->priv_lock);
}
static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
@@ -558,22 +459,35 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
{
struct device *dev = &urb->dev->dev;
int ret = 0;
- unsigned long flags;
+ struct vhci_device *vdev;
usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
- hcd, urb, mem_flags);
+ hcd, urb, mem_flags);
/* patch to usb_sg_init() is in 2.5.60 */
BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock(&the_controller->lock);
if (urb->status != -EINPROGRESS) {
dev_err(dev, "URB already unlinked!, status %d\n", urb->status);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&the_controller->lock);
return urb->status;
}
+ vdev = port_to_vdev(urb->dev->portnum-1);
+
+ /* refuse enqueue for dead connection */
+ spin_lock(&vdev->ud.lock);
+ if (vdev->ud.status == VDEV_ST_NULL ||
+ vdev->ud.status == VDEV_ST_ERROR) {
+ dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport);
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock(&the_controller->lock);
+ return -ENODEV;
+ }
+ spin_unlock(&vdev->ud.lock);
+
ret = usb_hcd_link_urb_to_ep(hcd, urb);
if (ret)
goto no_need_unlink;
@@ -587,13 +501,10 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
* 2. Set_Address request to DevAddr(0) EndPoint(0)
*
*/
-
if (usb_pipedevice(urb->pipe) == 0) {
__u8 type = usb_pipetype(urb->pipe);
struct usb_ctrlrequest *ctrlreq =
- (struct usb_ctrlrequest *) urb->setup_packet;
- struct vhci_device *vdev =
- port_to_vdev(the_controller->pending_port);
+ (struct usb_ctrlrequest *) urb->setup_packet;
if (type != PIPE_CONTROL || !ctrlreq) {
dev_err(dev, "invalid request to devnum 0\n");
@@ -607,7 +518,9 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
dev_info(dev, "SetAddress Request (%d) to port %d\n",
ctrlreq->wValue, vdev->rhport);
- vdev->udev = urb->dev;
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = usb_get_dev(urb->dev);
spin_lock(&vdev->ud.lock);
vdev->ud.status = VDEV_ST_USED;
@@ -622,19 +535,20 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
goto no_need_xmit;
case USB_REQ_GET_DESCRIPTOR:
- if (ctrlreq->wValue == (USB_DT_DEVICE << 8))
- usbip_dbg_vhci_hc("Not yet?: "
- "Get_Descriptor to device 0 "
- "(get max pipe size)\n");
+ if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8))
+ usbip_dbg_vhci_hc(
+ "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n");
- /* FIXME: reference count? (usb_get_dev()) */
- vdev->udev = urb->dev;
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = usb_get_dev(urb->dev);
goto out;
default:
/* NOT REACHED */
- dev_err(dev, "invalid request to devnum 0 bRequest %u, "
- "wValue %u\n", ctrlreq->bRequest,
+ dev_err(dev,
+ "invalid request to devnum 0 bRequest %u, wValue %u\n",
+ ctrlreq->bRequest,
ctrlreq->wValue);
ret = -EINVAL;
goto no_need_xmit;
@@ -644,18 +558,15 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
out:
vhci_tx_urb(urb);
-
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&the_controller->lock);
return 0;
no_need_xmit:
usb_hcd_unlink_urb_from_ep(hcd, urb);
no_need_unlink:
- spin_unlock_irqrestore(&the_controller->lock, flags);
-
+ spin_unlock(&the_controller->lock);
usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
-
return ret;
}
@@ -707,28 +618,27 @@ no_need_unlink:
*/
static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
- unsigned long flags;
struct vhci_priv *priv;
struct vhci_device *vdev;
- usbip_uinfo("vhci_hcd: dequeue a urb %p\n", urb);
+ pr_info("dequeue a urb %p\n", urb);
-
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock(&the_controller->lock);
priv = urb->hcpriv;
if (!priv) {
/* URB was never linked! or will be soon given back by
* vhci_rx. */
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&the_controller->lock);
return 0;
}
{
int ret = 0;
+
ret = usb_hcd_check_unlink_urb(hcd, urb, status);
if (ret) {
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&the_controller->lock);
return ret;
}
}
@@ -738,105 +648,117 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
if (!vdev->ud.tcp_socket) {
/* tcp connection is closed */
- unsigned long flags2;
+ spin_lock(&vdev->priv_lock);
- spin_lock_irqsave(&vdev->priv_lock, flags2);
-
- usbip_uinfo("vhci_hcd: device %p seems to be disconnected\n",
- vdev);
+ pr_info("device %p seems to be disconnected\n", vdev);
list_del(&priv->list);
kfree(priv);
urb->hcpriv = NULL;
- spin_unlock_irqrestore(&vdev->priv_lock, flags2);
+ spin_unlock(&vdev->priv_lock);
/*
* If tcp connection is alive, we have sent CMD_UNLINK.
* vhci_rx will receive RET_UNLINK and give back the URB.
* Otherwise, we give back it here.
*/
- usbip_uinfo("vhci_hcd: vhci_urb_dequeue() gives back urb %p\n",
- urb);
+ pr_info("gives back urb %p\n", urb);
usb_hcd_unlink_urb_from_ep(hcd, urb);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&the_controller->lock);
usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
- spin_lock_irqsave(&the_controller->lock, flags);
+ urb->status);
+ spin_lock(&the_controller->lock);
} else {
/* tcp connection is alive */
- unsigned long flags2;
struct vhci_unlink *unlink;
- spin_lock_irqsave(&vdev->priv_lock, flags2);
+ spin_lock(&vdev->priv_lock);
/* setup CMD_UNLINK pdu */
unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC);
if (!unlink) {
- usbip_uerr("malloc vhci_unlink\n");
- spin_unlock_irqrestore(&vdev->priv_lock, flags2);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&vdev->priv_lock);
+ spin_unlock(&the_controller->lock);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
return -ENOMEM;
}
unlink->seqnum = atomic_inc_return(&the_controller->seqnum);
if (unlink->seqnum == 0xffff)
- usbip_uinfo("seqnum max\n");
+ pr_info("seqnum max\n");
unlink->unlink_seqnum = priv->seqnum;
- usbip_uinfo("vhci_hcd: device %p seems to be still connected\n",
- vdev);
+ pr_info("device %p seems to be still connected\n", vdev);
/* send cmd_unlink and try to cancel the pending URB in the
* peer */
list_add_tail(&unlink->list, &vdev->unlink_tx);
wake_up(&vdev->waitq_tx);
- spin_unlock_irqrestore(&vdev->priv_lock, flags2);
- }
-
-
- if (!vdev->ud.tcp_socket) {
- /* tcp connection is closed */
- usbip_uinfo("vhci_hcd: vhci_urb_dequeue() gives back urb %p\n",
- urb);
-
- usb_hcd_unlink_urb_from_ep(hcd, urb);
-
- spin_unlock_irqrestore(&the_controller->lock, flags);
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_unlock(&vdev->priv_lock);
}
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock(&the_controller->lock);
usbip_dbg_vhci_hc("leave\n");
return 0;
}
-
static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
{
struct vhci_unlink *unlink, *tmp;
+ spin_lock(&the_controller->lock);
spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
+ pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum);
list_del(&unlink->list);
kfree(unlink);
}
- list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
+ while (!list_empty(&vdev->unlink_rx)) {
+ struct urb *urb;
+
+ unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink,
+ list);
+
+ /* give back URB of unanswered unlink request */
+ pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum);
+
+ urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
+ if (!urb) {
+ pr_info("the urb (seqnum %lu) was already given back\n",
+ unlink->unlink_seqnum);
+ list_del(&unlink->list);
+ kfree(unlink);
+ continue;
+ }
+
+ urb->status = -ENODEV;
+
+ usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+
list_del(&unlink->list);
+
+ spin_unlock(&vdev->priv_lock);
+ spin_unlock(&the_controller->lock);
+
+ usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
+ urb->status);
+
+ spin_lock(&the_controller->lock);
+ spin_lock(&vdev->priv_lock);
+
kfree(unlink);
}
spin_unlock(&vdev->priv_lock);
+ spin_unlock(&the_controller->lock);
}
/*
@@ -850,19 +772,27 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
/* need this? see stub_dev.c */
if (ud->tcp_socket) {
- usbip_udbg("shutdown tcp_socket %p\n", ud->tcp_socket);
+ pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket);
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
- usbip_stop_threads(&vdev->ud);
- usbip_uinfo("stop threads\n");
+ /* kill threads related to this sdev */
+ if (vdev->ud.tcp_rx) {
+ kthread_stop_put(vdev->ud.tcp_rx);
+ vdev->ud.tcp_rx = NULL;
+ }
+ if (vdev->ud.tcp_tx) {
+ kthread_stop_put(vdev->ud.tcp_tx);
+ vdev->ud.tcp_tx = NULL;
+ }
+ pr_info("stop threads\n");
/* active connection is closed */
- if (vdev->ud.tcp_socket != NULL) {
- sock_release(vdev->ud.tcp_socket);
+ if (vdev->ud.tcp_socket) {
+ sockfd_put(vdev->ud.tcp_socket);
vdev->ud.tcp_socket = NULL;
}
- usbip_uinfo("release socket\n");
+ pr_info("release socket\n");
vhci_device_unlink_cleanup(vdev);
@@ -874,11 +804,11 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
* disable endpoints. pending urbs are unlinked(dequeued).
*
* NOTE: After calling rh_port_disconnect(), the USB device drivers of a
- * deteched device should release used urbs in a cleanup function(i.e.
+ * detached device should release used urbs in a cleanup function (i.e.
* xxx_disconnect()). Therefore, vhci_hcd does not need to release
* pushed urbs and their private data in this function.
*
- * NOTE: vhci_dequeue() must be considered carefully. When shutdowning
+ * NOTE: vhci_dequeue() must be considered carefully. When shutting down
* a connection, vhci_shutdown_connection() expects vhci_dequeue()
* gives back pushed urbs and frees their private data by request of
* the cleanup function of a USB driver. When unlinking a urb with an
@@ -888,7 +818,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
*/
rh_port_disconnect(vdev->rhport);
- usbip_uinfo("disconnect device\n");
+ pr_info("disconnect device\n");
}
@@ -901,8 +831,14 @@ static void vhci_device_reset(struct usbip_device *ud)
vdev->speed = 0;
vdev->devid = 0;
- ud->tcp_socket = NULL;
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = NULL;
+ if (ud->tcp_socket) {
+ sockfd_put(ud->tcp_socket);
+ ud->tcp_socket = NULL;
+ }
ud->status = VDEV_ST_NULL;
spin_unlock(&ud->lock);
@@ -911,9 +847,7 @@ static void vhci_device_reset(struct usbip_device *ud)
static void vhci_device_unusable(struct usbip_device *ud)
{
spin_lock(&ud->lock);
-
ud->status = VDEV_ST_ERROR;
-
spin_unlock(&ud->lock);
}
@@ -921,19 +855,14 @@ static void vhci_device_init(struct vhci_device *vdev)
{
memset(vdev, 0, sizeof(*vdev));
- usbip_task_init(&vdev->ud.tcp_rx, "vhci_rx", vhci_rx_loop);
- usbip_task_init(&vdev->ud.tcp_tx, "vhci_tx", vhci_tx_loop);
-
vdev->ud.side = USBIP_VHCI;
vdev->ud.status = VDEV_ST_NULL;
- /* vdev->ud.lock = SPIN_LOCK_UNLOCKED; */
spin_lock_init(&vdev->ud.lock);
INIT_LIST_HEAD(&vdev->priv_rx);
INIT_LIST_HEAD(&vdev->priv_tx);
INIT_LIST_HEAD(&vdev->unlink_tx);
INIT_LIST_HEAD(&vdev->unlink_rx);
- /* vdev->priv_lock = SPIN_LOCK_UNLOCKED; */
spin_lock_init(&vdev->priv_lock);
init_waitqueue_head(&vdev->waitq_tx);
@@ -945,9 +874,6 @@ static void vhci_device_init(struct vhci_device *vdev)
usbip_start_eh(&vdev->ud);
}
-
-/*----------------------------------------------------------------------*/
-
static int vhci_start(struct usb_hcd *hcd)
{
struct vhci_hcd *vhci = hcd_to_vhci(hcd);
@@ -956,11 +882,11 @@ static int vhci_start(struct usb_hcd *hcd)
usbip_dbg_vhci_hc("enter vhci_start\n");
-
/* initialize private data of usb_hcd */
for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
struct vhci_device *vdev = &vhci->vdev[rhport];
+
vhci_device_init(vdev);
vdev->rhport = rhport;
}
@@ -968,17 +894,13 @@ static int vhci_start(struct usb_hcd *hcd)
atomic_set(&vhci->seqnum, 0);
spin_lock_init(&vhci->lock);
-
-
hcd->power_budget = 0; /* no limit */
- hcd->state = HC_STATE_RUNNING;
hcd->uses_new_polling = 1;
-
/* vhci_hcd is now ready to be controlled through sysfs */
err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
if (err) {
- usbip_uerr("create sysfs files\n");
+ pr_err("create sysfs files\n");
return err;
}
@@ -992,31 +914,24 @@ static void vhci_stop(struct usb_hcd *hcd)
usbip_dbg_vhci_hc("stop VHCI controller\n");
-
/* 1. remove the userland interface of vhci_hcd */
sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
/* 2. shutdown all the ports of vhci_hcd */
- for (rhport = 0 ; rhport < VHCI_NPORTS; rhport++) {
+ for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
struct vhci_device *vdev = &vhci->vdev[rhport];
usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED);
usbip_stop_eh(&vdev->ud);
}
-
-
- usbip_uinfo("vhci_stop done\n");
}
-/*----------------------------------------------------------------------*/
-
static int vhci_get_frame_number(struct usb_hcd *hcd)
{
- usbip_uerr("Not yet implemented\n");
+ pr_err("Not yet implemented\n");
return 0;
}
-
#ifdef CONFIG_PM
/* FIXME: suspend/resume */
@@ -1026,11 +941,9 @@ static int vhci_bus_suspend(struct usb_hcd *hcd)
dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
- spin_lock_irq(&vhci->lock);
- /* vhci->rh_state = DUMMY_RH_SUSPENDED;
- * set_link_state(vhci); */
+ spin_lock(&vhci->lock);
hcd->state = HC_STATE_SUSPENDED;
- spin_unlock_irq(&vhci->lock);
+ spin_unlock(&vhci->lock);
return 0;
}
@@ -1042,20 +955,14 @@ static int vhci_bus_resume(struct usb_hcd *hcd)
dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
- spin_lock_irq(&vhci->lock);
- if (!HCD_HW_ACCESSIBLE(hcd)) {
+ spin_lock(&vhci->lock);
+ if (!HCD_HW_ACCESSIBLE(hcd))
rc = -ESHUTDOWN;
- } else {
- /* vhci->rh_state = DUMMY_RH_RUNNING;
- * set_link_state(vhci);
- * if (!list_empty(&vhci->urbp_list))
- * mod_timer(&vhci->timer, jiffies); */
+ else
hcd->state = HC_STATE_RUNNING;
- }
- spin_unlock_irq(&vhci->lock);
- return rc;
+ spin_unlock(&vhci->lock);
- return 0;
+ return rc;
}
#else
@@ -1064,8 +971,6 @@ static int vhci_bus_resume(struct usb_hcd *hcd)
#define vhci_bus_resume NULL
#endif
-
-
static struct hc_driver vhci_hc_driver = {
.description = driver_name,
.product_desc = driver_desc,
@@ -1092,26 +997,18 @@ static int vhci_hcd_probe(struct platform_device *pdev)
struct usb_hcd *hcd;
int ret;
- usbip_uinfo("proving...\n");
-
usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id);
- /* will be removed */
- if (pdev->dev.dma_mask) {
- dev_info(&pdev->dev, "vhci_hcd DMA not supported\n");
- return -EINVAL;
- }
-
/*
* Allocate and initialize hcd.
* Our private data is also allocated automatically.
*/
hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
- usbip_uerr("create hcd failed\n");
+ pr_err("create hcd failed\n");
return -ENOMEM;
}
-
+ hcd->has_tt = 1;
/* this is private data for vhci_hcd */
the_controller = hcd_to_vhci(hcd);
@@ -1122,18 +1019,16 @@ static int vhci_hcd_probe(struct platform_device *pdev)
*/
ret = usb_add_hcd(hcd, 0, 0);
if (ret != 0) {
- usbip_uerr("usb_add_hcd failed %d\n", ret);
+ pr_err("usb_add_hcd failed %d\n", ret);
usb_put_hcd(hcd);
the_controller = NULL;
return ret;
}
-
usbip_dbg_vhci_hc("bye\n");
return 0;
}
-
static int vhci_hcd_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd;
@@ -1151,12 +1046,9 @@ static int vhci_hcd_remove(struct platform_device *pdev)
usb_put_hcd(hcd);
the_controller = NULL;
-
return 0;
}
-
-
#ifdef CONFIG_PM
/* what should happen for USB/IP under suspend/resume? */
@@ -1167,25 +1059,24 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
int connected = 0;
int ret = 0;
- dev_dbg(&pdev->dev, "%s\n", __func__);
-
hcd = platform_get_drvdata(pdev);
spin_lock(&the_controller->lock);
for (rhport = 0; rhport < VHCI_NPORTS; rhport++)
if (the_controller->port_status[rhport] &
- USB_PORT_STAT_CONNECTION)
+ USB_PORT_STAT_CONNECTION)
connected += 1;
spin_unlock(&the_controller->lock);
if (connected > 0) {
- usbip_uinfo("We have %d active connection%s. Do not suspend.\n",
- connected, (connected == 1 ? "" : "s"));
+ dev_info(&pdev->dev,
+ "We have %d active connection%s. Do not suspend.\n",
+ connected, (connected == 1 ? "" : "s"));
ret = -EBUSY;
} else {
- usbip_uinfo("suspend vhci_hcd");
+ dev_info(&pdev->dev, "suspend vhci_hcd");
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
}
@@ -1212,20 +1103,17 @@ static int vhci_hcd_resume(struct platform_device *pdev)
#endif
-
static struct platform_driver vhci_driver = {
.probe = vhci_hcd_probe,
- .remove = __devexit_p(vhci_hcd_remove),
+ .remove = vhci_hcd_remove,
.suspend = vhci_hcd_suspend,
.resume = vhci_hcd_resume,
.driver = {
- .name = (char *) driver_name,
+ .name = driver_name,
.owner = THIS_MODULE,
},
};
-/*----------------------------------------------------------------------*/
-
/*
* The VHCI 'device' is 'virtual'; not a real plug&play hardware.
* We need to add this virtual device as a platform device arbitrarily:
@@ -1238,53 +1126,47 @@ static void the_pdev_release(struct device *dev)
static struct platform_device the_pdev = {
/* should be the same name as driver_name */
- .name = (char *) driver_name,
+ .name = driver_name,
.id = -1,
.dev = {
- /* .driver = &vhci_driver, */
.release = the_pdev_release,
},
};
-static int __init vhci_init(void)
+static int __init vhci_hcd_init(void)
{
int ret;
- usbip_dbg_vhci_hc("enter\n");
if (usb_disabled())
return -ENODEV;
- printk(KERN_INFO KBUILD_MODNAME ": %s, %s\n", driver_name,
- DRIVER_VERSION);
-
ret = platform_driver_register(&vhci_driver);
- if (ret < 0)
+ if (ret)
goto err_driver_register;
ret = platform_device_register(&the_pdev);
- if (ret < 0)
+ if (ret)
goto err_platform_device_register;
- usbip_dbg_vhci_hc("bye\n");
+ pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
return ret;
- /* error occurred */
err_platform_device_register:
platform_driver_unregister(&vhci_driver);
-
err_driver_register:
- usbip_dbg_vhci_hc("bye\n");
return ret;
}
-module_init(vhci_init);
-static void __exit vhci_cleanup(void)
+static void __exit vhci_hcd_exit(void)
{
- usbip_dbg_vhci_hc("enter\n");
-
platform_device_unregister(&the_pdev);
platform_driver_unregister(&vhci_driver);
-
- usbip_dbg_vhci_hc("bye\n");
}
-module_exit(vhci_cleanup);
+
+module_init(vhci_hcd_init);
+module_exit(vhci_hcd_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(USBIP_VERSION);
diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c
index 8147d7202b2..d07fcb5ee93 100644
--- a/drivers/staging/usbip/vhci_rx.c
+++ b/drivers/staging/usbip/vhci_rx.c
@@ -17,95 +17,91 @@
* USA.
*/
+#include <linux/kthread.h>
#include <linux/slab.h>
#include "usbip_common.h"
#include "vhci.h"
-
-/* get URB from transmitted urb queue */
-static struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
- __u32 seqnum)
+/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */
+struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum)
{
struct vhci_priv *priv, *tmp;
struct urb *urb = NULL;
int status;
- spin_lock(&vdev->priv_lock);
-
list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
- if (priv->seqnum == seqnum) {
- urb = priv->urb;
- status = urb->status;
-
- usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n",
- urb, priv, seqnum);
-
- /* TODO: fix logic here to improve indent situtation */
- if (status != -EINPROGRESS) {
- if (status == -ENOENT ||
- status == -ECONNRESET)
- dev_info(&urb->dev->dev,
- "urb %p was unlinked "
- "%ssynchronuously.\n", urb,
- status == -ENOENT ? "" : "a");
- else
- dev_info(&urb->dev->dev,
- "urb %p may be in a error, "
- "status %d\n", urb, status);
- }
-
- list_del(&priv->list);
- kfree(priv);
- urb->hcpriv = NULL;
-
+ if (priv->seqnum != seqnum)
+ continue;
+
+ urb = priv->urb;
+ status = urb->status;
+
+ usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n",
+ urb, priv, seqnum);
+
+ switch (status) {
+ case -ENOENT:
+ /* fall through */
+ case -ECONNRESET:
+ dev_info(&urb->dev->dev,
+ "urb %p was unlinked %ssynchronuously.\n", urb,
+ status == -ENOENT ? "" : "a");
+ break;
+ case -EINPROGRESS:
+ /* no info output */
break;
+ default:
+ dev_info(&urb->dev->dev,
+ "urb %p may be in a error, status %d\n", urb,
+ status);
}
- }
- spin_unlock(&vdev->priv_lock);
+ list_del(&priv->list);
+ kfree(priv);
+ urb->hcpriv = NULL;
+
+ break;
+ }
return urb;
}
static void vhci_recv_ret_submit(struct vhci_device *vdev,
- struct usbip_header *pdu)
+ struct usbip_header *pdu)
{
struct usbip_device *ud = &vdev->ud;
struct urb *urb;
-
+ spin_lock(&vdev->priv_lock);
urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum);
-
+ spin_unlock(&vdev->priv_lock);
if (!urb) {
- usbip_uerr("cannot find a urb of seqnum %u\n",
- pdu->base.seqnum);
- usbip_uinfo("max seqnum %d\n",
- atomic_read(&the_controller->seqnum));
+ pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
+ pr_info("max seqnum %d\n",
+ atomic_read(&the_controller->seqnum));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
return;
}
-
/* unpack the pdu to a urb */
usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0);
-
/* recv transfer buffer */
if (usbip_recv_xbuff(ud, urb) < 0)
return;
-
/* recv iso_packet_descriptor */
if (usbip_recv_iso(ud, urb) < 0)
return;
+ /* restore the padding in iso packets */
+ usbip_pad_iso(ud, urb);
if (usbip_dbg_flag_vhci_rx)
usbip_dump_urb(urb);
-
usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
spin_lock(&the_controller->lock);
@@ -114,25 +110,23 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
-
usbip_dbg_vhci_rx("Leave\n");
return;
}
-
static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
- struct usbip_header *pdu)
+ struct usbip_header *pdu)
{
struct vhci_unlink *unlink, *tmp;
spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
- usbip_uinfo("unlink->seqnum %lu\n", unlink->seqnum);
+ pr_info("unlink->seqnum %lu\n", unlink->seqnum);
if (unlink->seqnum == pdu->base.seqnum) {
usbip_dbg_vhci_rx("found pending unlink, %lu\n",
- unlink->seqnum);
+ unlink->seqnum);
list_del(&unlink->list);
spin_unlock(&vdev->priv_lock);
@@ -145,9 +139,8 @@ static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
return NULL;
}
-
static void vhci_recv_ret_unlink(struct vhci_device *vdev,
- struct usbip_header *pdu)
+ struct usbip_header *pdu)
{
struct vhci_unlink *unlink;
struct urb *urb;
@@ -156,38 +149,50 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
unlink = dequeue_pending_unlink(vdev, pdu);
if (!unlink) {
- usbip_uinfo("cannot find the pending unlink %u\n",
- pdu->base.seqnum);
+ pr_info("cannot find the pending unlink %u\n",
+ pdu->base.seqnum);
return;
}
+ spin_lock(&vdev->priv_lock);
urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
+ spin_unlock(&vdev->priv_lock);
+
if (!urb) {
/*
* I get the result of a unlink request. But, it seems that I
* already received the result of its submit result and gave
* back the URB.
*/
- usbip_uinfo("the urb (seqnum %d) was already given backed\n",
- pdu->base.seqnum);
+ pr_info("the urb (seqnum %d) was already given back\n",
+ pdu->base.seqnum);
} else {
usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
- /* If unlink is succeed, status is -ECONNRESET */
+ /* If unlink is successful, status is -ECONNRESET */
urb->status = pdu->u.ret_unlink.status;
- usbip_uinfo("%d\n", urb->status);
+ pr_info("urb->status %d\n", urb->status);
spin_lock(&the_controller->lock);
usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
spin_unlock(&the_controller->lock);
usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
+ urb->status);
}
kfree(unlink);
+}
- return;
+static int vhci_priv_tx_empty(struct vhci_device *vdev)
+{
+ int empty = 0;
+
+ spin_lock(&vdev->priv_lock);
+ empty = list_empty(&vdev->priv_rx);
+ spin_unlock(&vdev->priv_lock);
+
+ return empty;
}
/* recv a pdu */
@@ -197,17 +202,34 @@ static void vhci_rx_pdu(struct usbip_device *ud)
struct usbip_header pdu;
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
-
usbip_dbg_vhci_rx("Enter\n");
memset(&pdu, 0, sizeof(pdu));
+ /* receive a pdu header */
+ ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
+ if (ret < 0) {
+ if (ret == -ECONNRESET)
+ pr_info("connection reset by peer\n");
+ else if (ret == -EAGAIN) {
+ /* ignore if connection was idle */
+ if (vhci_priv_tx_empty(vdev))
+ return;
+ pr_info("connection timed out with pending urbs\n");
+ } else if (ret != -ERESTARTSYS)
+ pr_info("xmit failed %d\n", ret);
- /* 1. receive a pdu header */
- ret = usbip_xmit(0, ud->tcp_socket, (char *) &pdu, sizeof(pdu), 0);
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return;
+ }
+ if (ret == 0) {
+ pr_info("connection closed");
+ usbip_event_add(ud, VDEV_EVENT_DOWN);
+ return;
+ }
if (ret != sizeof(pdu)) {
- usbip_uerr("receiving pdu failed! size is %d, should be %d\n",
- ret, (unsigned int)sizeof(pdu));
+ pr_err("received pdu size is %d, should be %d\n", ret,
+ (unsigned int)sizeof(pdu));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
return;
}
@@ -225,32 +247,24 @@ static void vhci_rx_pdu(struct usbip_device *ud)
vhci_recv_ret_unlink(vdev, &pdu);
break;
default:
- /* NOTREACHED */
- usbip_uerr("unknown pdu %u\n", pdu.base.command);
+ /* NOT REACHED */
+ pr_err("unknown pdu %u\n", pdu.base.command);
usbip_dump_header(&pdu);
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ break;
}
}
-
-/*-------------------------------------------------------------------------*/
-
-void vhci_rx_loop(struct usbip_task *ut)
+int vhci_rx_loop(void *data)
{
- struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_rx);
-
-
- while (1) {
- if (signal_pending(current)) {
- usbip_dbg_vhci_rx("signal catched!\n");
- break;
- }
-
+ struct usbip_device *ud = data;
+ while (!kthread_should_stop()) {
if (usbip_event_happened(ud))
break;
vhci_rx_pdu(ud);
}
-}
+ return 0;
+}
diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
index f6e34e03c8e..211f43f67ea 100644
--- a/drivers/staging/usbip/vhci_sysfs.c
+++ b/drivers/staging/usbip/vhci_sysfs.c
@@ -17,15 +17,17 @@
* USA.
*/
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/net.h>
+
#include "usbip_common.h"
#include "vhci.h"
-#include <linux/in.h>
-
/* TODO: refine locking ?*/
/* Sysfs entry to show port status */
-static ssize_t show_status(struct device *dev, struct device_attribute *attr,
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
char *out)
{
char *s = out;
@@ -45,27 +47,26 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr,
* 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");
+ out += sprintf(out,
+ "prt sta spd bus dev socket local_busid\n");
for (i = 0; i < VHCI_NPORTS; i++) {
struct vhci_device *vdev = port_to_vdev(i);
spin_lock(&vdev->ud.lock);
-
out += sprintf(out, "%03u %03u ", i, vdev->ud.status);
if (vdev->ud.status == VDEV_ST_USED) {
out += sprintf(out, "%03u %08x ",
- vdev->speed, vdev->devid);
+ vdev->speed, vdev->devid);
out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
- } else
+ } else {
out += sprintf(out, "000 000 000 0000000000000000 0-0");
+ }
out += sprintf(out, "\n");
-
spin_unlock(&vdev->ud.lock);
}
@@ -73,7 +74,7 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr,
return out - s;
}
-static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+static DEVICE_ATTR_RO(status);
/* Sysfs entry to shutdown a virtual connection */
static int vhci_port_disconnect(__u32 rhport)
@@ -89,7 +90,7 @@ static int vhci_port_disconnect(__u32 rhport)
spin_lock(&vdev->ud.lock);
if (vdev->ud.status == VDEV_ST_NULL) {
- usbip_uerr("not connected %d\n", vdev->ud.status);
+ pr_err("not connected %d\n", vdev->ud.status);
/* unlock */
spin_unlock(&vdev->ud.lock);
@@ -113,11 +114,12 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
int err;
__u32 rhport = 0;
- sscanf(buf, "%u", &rhport);
+ if (sscanf(buf, "%u", &rhport) != 1)
+ return -EINVAL;
/* check rhport */
if (rhport >= VHCI_NPORTS) {
- usbip_uerr("invalid port %u\n", rhport);
+ dev_err(dev, "invalid port %u\n", rhport);
return -EINVAL;
}
@@ -126,6 +128,7 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
return -EINVAL;
usbip_dbg_vhci_sysfs("Leave\n");
+
return count;
}
static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
@@ -134,8 +137,8 @@ static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
static int valid_args(__u32 rhport, enum usb_device_speed speed)
{
/* check rhport */
- if ((rhport < 0) || (rhport >= VHCI_NPORTS)) {
- usbip_uerr("port %u\n", rhport);
+ if (rhport >= VHCI_NPORTS) {
+ pr_err("port %u\n", rhport);
return -EINVAL;
}
@@ -147,7 +150,8 @@ static int valid_args(__u32 rhport, enum usb_device_speed speed)
case USB_SPEED_WIRELESS:
break;
default:
- usbip_uerr("speed %d\n", speed);
+ pr_err("Failed attach request for unsupported USB speed: %s\n",
+ usb_speed_string(speed));
return -EINVAL;
}
@@ -172,6 +176,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
struct socket *socket;
int sockfd = 0;
__u32 rhport = 0, devid = 0, speed = 0;
+ int err;
/*
* @rhport: port number of vhci_hcd
@@ -179,28 +184,26 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
* @devid: unique device identifier in a remote host
* @speed: usb device speed in a remote host
*/
- sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed);
+ if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4)
+ return -EINVAL;
usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
- rhport, sockfd, devid, speed);
-
+ rhport, sockfd, devid, speed);
/* check received parameters */
if (valid_args(rhport, speed) < 0)
return -EINVAL;
- /* check sockfd */
- socket = sockfd_to_socket(sockfd);
+ /* Extract socket from fd. */
+ socket = sockfd_lookup(sockfd, &err);
if (!socket)
- return -EINVAL;
+ return -EINVAL;
/* now need lock until setting vdev status as used */
/* begin a lock */
spin_lock(&the_controller->lock);
-
vdev = port_to_vdev(rhport);
-
spin_lock(&vdev->ud.lock);
if (vdev->ud.status != VDEV_ST_NULL) {
@@ -208,12 +211,15 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
- usbip_uerr("port %d already used\n", rhport);
+ sockfd_put(socket);
+
+ dev_err(dev, "port %d already used\n", rhport);
return -EINVAL;
}
- usbip_uinfo("rhport(%u) sockfd(%d) devid(%u) speed(%u)\n",
- rhport, sockfd, devid, speed);
+ 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;
@@ -224,11 +230,8 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
spin_unlock(&the_controller->lock);
/* end the lock */
- /*
- * this function will sleep, so should be out of the lock. but, it's ok
- * because we already marked vdev as being used. really?
- */
- usbip_start_threads(&vdev->ud);
+ vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
+ vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
rh_port_connect(rhport, speed);
@@ -244,6 +247,6 @@ static struct attribute *dev_attrs[] = {
NULL,
};
-struct attribute_group dev_attr_group = {
+const struct attribute_group dev_attr_group = {
.attrs = dev_attrs,
};
diff --git a/drivers/staging/usbip/vhci_tx.c b/drivers/staging/usbip/vhci_tx.c
index e1c1f716a1c..409fd99f325 100644
--- a/drivers/staging/usbip/vhci_tx.c
+++ b/drivers/staging/usbip/vhci_tx.c
@@ -17,28 +17,26 @@
* USA.
*/
+#include <linux/kthread.h>
#include <linux/slab.h>
#include "usbip_common.h"
#include "vhci.h"
-
static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb)
{
struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv);
struct vhci_device *vdev = priv->vdev;
usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n",
- usb_pipedevice(urb->pipe), vdev->devid);
+ usb_pipedevice(urb->pipe), vdev->devid);
- pdup->base.command = USBIP_CMD_SUBMIT;
- pdup->base.seqnum = priv->seqnum;
- pdup->base.devid = vdev->devid;
- if (usb_pipein(urb->pipe))
- pdup->base.direction = USBIP_DIR_IN;
- else
- pdup->base.direction = USBIP_DIR_OUT;
- pdup->base.ep = usb_pipeendpoint(urb->pipe);
+ pdup->base.command = USBIP_CMD_SUBMIT;
+ pdup->base.seqnum = priv->seqnum;
+ pdup->base.devid = vdev->devid;
+ pdup->base.direction = usb_pipein(urb->pipe) ?
+ USBIP_DIR_IN : USBIP_DIR_OUT;
+ pdup->base.ep = usb_pipeendpoint(urb->pipe);
usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1);
@@ -48,24 +46,21 @@ static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb)
static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev)
{
- unsigned long flags;
struct vhci_priv *priv, *tmp;
- spin_lock_irqsave(&vdev->priv_lock, flags);
+ spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) {
list_move_tail(&priv->list, &vdev->priv_rx);
- spin_unlock_irqrestore(&vdev->priv_lock, flags);
+ spin_unlock(&vdev->priv_lock);
return priv;
}
- spin_unlock_irqrestore(&vdev->priv_lock, flags);
+ spin_unlock(&vdev->priv_lock);
return NULL;
}
-
-
static int vhci_send_cmd_submit(struct vhci_device *vdev)
{
struct vhci_priv *priv = NULL;
@@ -80,7 +75,7 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
int ret;
struct urb *urb = priv->urb;
struct usbip_header pdu_header;
- void *iso_buffer = NULL;
+ struct usbip_iso_packet_descriptor *iso_buffer = NULL;
txsize = 0;
memset(&pdu_header, 0, sizeof(pdu_header));
@@ -89,7 +84,6 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
usbip_dbg_vhci_tx("setup txdata urb %p\n", urb);
-
/* 1. setup usbip_header */
setup_cmd_submit_pdu(&pdu_header, urb);
usbip_header_correct_endian(&pdu_header, 1);
@@ -123,8 +117,8 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
if (ret != txsize) {
- usbip_uerr("sendmsg failed!, retval %d for %zd\n", ret,
- txsize);
+ pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
+ txsize);
kfree(iso_buffer);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
return -1;
@@ -139,23 +133,19 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev)
return total_size;
}
-
-/*-------------------------------------------------------------------------*/
-
static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)
{
- unsigned long flags;
struct vhci_unlink *unlink, *tmp;
- spin_lock_irqsave(&vdev->priv_lock, flags);
+ spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
list_move_tail(&unlink->list, &vdev->unlink_rx);
- spin_unlock_irqrestore(&vdev->priv_lock, flags);
+ spin_unlock(&vdev->priv_lock);
return unlink;
}
- spin_unlock_irqrestore(&vdev->priv_lock, flags);
+ spin_unlock(&vdev->priv_lock);
return NULL;
}
@@ -181,7 +171,6 @@ static int vhci_send_cmd_unlink(struct vhci_device *vdev)
usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum);
-
/* 1. setup usbip_header */
pdu_header.base.command = USBIP_CMD_UNLINK;
pdu_header.base.seqnum = unlink->seqnum;
@@ -197,13 +186,12 @@ static int vhci_send_cmd_unlink(struct vhci_device *vdev)
ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize);
if (ret != txsize) {
- usbip_uerr("sendmsg failed!, retval %d for %zd\n", ret,
- txsize);
+ pr_err("sendmsg failed!, ret=%d for %zd\n", ret,
+ txsize);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
return -1;
}
-
usbip_dbg_vhci_tx("send txdata\n");
total_size += txsize;
@@ -212,20 +200,12 @@ static int vhci_send_cmd_unlink(struct vhci_device *vdev)
return total_size;
}
-
-/*-------------------------------------------------------------------------*/
-
-void vhci_tx_loop(struct usbip_task *ut)
+int vhci_tx_loop(void *data)
{
- struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_tx);
+ struct usbip_device *ud = data;
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
- while (1) {
- if (signal_pending(current)) {
- usbip_uinfo("vhci_tx signal catched\n");
- break;
- }
-
+ while (!kthread_should_stop()) {
if (vhci_send_cmd_submit(vdev) < 0)
break;
@@ -233,9 +213,12 @@ void vhci_tx_loop(struct usbip_task *ut)
break;
wait_event_interruptible(vdev->waitq_tx,
- (!list_empty(&vdev->priv_tx) ||
- !list_empty(&vdev->unlink_tx)));
+ (!list_empty(&vdev->priv_tx) ||
+ !list_empty(&vdev->unlink_tx) ||
+ kthread_should_stop()));
usbip_dbg_vhci_tx("pending urbs ?, now wake up\n");
}
+
+ return 0;
}