diff options
Diffstat (limited to 'drivers/staging/usbip')
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; } |
