mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
41
drivers/usb/usbip/Kconfig
Normal file
41
drivers/usb/usbip/Kconfig
Normal file
|
@ -0,0 +1,41 @@
|
|||
config USBIP_CORE
|
||||
tristate "USB/IP support"
|
||||
depends on USB && NET
|
||||
---help---
|
||||
This enables pushing USB packets over IP to allow remote
|
||||
machines direct access to USB devices. It provides the
|
||||
USB/IP core that is required by both drivers.
|
||||
|
||||
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 USBIP_VHCI_HCD
|
||||
tristate "VHCI hcd"
|
||||
depends on USBIP_CORE
|
||||
---help---
|
||||
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.
|
||||
|
||||
config USBIP_HOST
|
||||
tristate "Host driver"
|
||||
depends on USBIP_CORE
|
||||
---help---
|
||||
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-host.
|
||||
|
||||
config USBIP_DEBUG
|
||||
bool "Debug messages for USB/IP"
|
||||
depends on USBIP_CORE
|
||||
---help---
|
||||
This enables the debug messages from the USB/IP drivers.
|
10
drivers/usb/usbip/Makefile
Normal file
10
drivers/usb/usbip/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_USBIP_CORE) += usbip-core.o
|
||||
usbip-core-y := usbip_common.o usbip_event.o
|
||||
|
||||
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
|
7
drivers/usb/usbip/README
Normal file
7
drivers/usb/usbip/README
Normal file
|
@ -0,0 +1,7 @@
|
|||
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>
|
113
drivers/usb/usbip/stub.h
Normal file
113
drivers/usb/usbip/stub.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifndef __USBIP_STUB_H
|
||||
#define __USBIP_STUB_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.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
|
||||
#define STUB_BUSID_ADDED 2
|
||||
#define STUB_BUSID_ALLOC 3
|
||||
|
||||
struct stub_device {
|
||||
struct usb_interface *interface;
|
||||
struct usb_device *udev;
|
||||
|
||||
struct usbip_device ud;
|
||||
__u32 devid;
|
||||
|
||||
/*
|
||||
* stub_priv preserves private data of each urb.
|
||||
* It is allocated as stub_priv_cache and assigned to urb->context.
|
||||
*
|
||||
* stub_priv is always linked to any one of 3 lists;
|
||||
* priv_init: linked to this until the comletion of a urb.
|
||||
* priv_tx : linked to this after the completion of a urb.
|
||||
* priv_free: linked to this after the sending of the result.
|
||||
*
|
||||
* Any of these list operations should be locked by priv_lock.
|
||||
*/
|
||||
spinlock_t priv_lock;
|
||||
struct list_head priv_init;
|
||||
struct list_head priv_tx;
|
||||
struct list_head priv_free;
|
||||
|
||||
/* see comments for unlinking in stub_rx.c */
|
||||
struct list_head unlink_tx;
|
||||
struct list_head unlink_free;
|
||||
|
||||
wait_queue_head_t tx_waitq;
|
||||
};
|
||||
|
||||
/* private data into urb->priv */
|
||||
struct stub_priv {
|
||||
unsigned long seqnum;
|
||||
struct list_head list;
|
||||
struct stub_device *sdev;
|
||||
struct urb *urb;
|
||||
|
||||
int unlinking;
|
||||
};
|
||||
|
||||
struct stub_unlink {
|
||||
unsigned long seqnum;
|
||||
struct list_head list;
|
||||
__u32 status;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
/* stub_dev.c */
|
||||
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 */
|
498
drivers/usb/usbip/stub_dev.c
Normal file
498
drivers/usb/usbip/stub_dev.c
Normal file
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "usbip_common.h"
|
||||
#include "stub.h"
|
||||
|
||||
/*
|
||||
* usbip_status shows the status of usbip-host as long as this driver is bound
|
||||
* to the target device.
|
||||
*/
|
||||
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;
|
||||
|
||||
if (!sdev) {
|
||||
dev_err(dev, "sdev is null\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_irq(&sdev->ud.lock);
|
||||
status = sdev->ud.status;
|
||||
spin_unlock_irq(&sdev->ud.lock);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", status);
|
||||
}
|
||||
static DEVICE_ATTR_RO(usbip_status);
|
||||
|
||||
/*
|
||||
* usbip_sockfd gets a socket descriptor of an established TCP connection that
|
||||
* is used to transfer usbip requests by kernel threads. -1 is a magic number
|
||||
* by which usbip connection is finished.
|
||||
*/
|
||||
static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
rv = sscanf(buf, "%d", &sockfd);
|
||||
if (rv != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (sockfd != -1) {
|
||||
int err;
|
||||
|
||||
dev_info(dev, "stub up\n");
|
||||
|
||||
spin_lock_irq(&sdev->ud.lock);
|
||||
|
||||
if (sdev->ud.status != SDEV_ST_AVAILABLE) {
|
||||
dev_err(dev, "not ready\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
socket = sockfd_lookup(sockfd, &err);
|
||||
if (!socket)
|
||||
goto err;
|
||||
|
||||
sdev->ud.tcp_socket = socket;
|
||||
|
||||
spin_unlock_irq(&sdev->ud.lock);
|
||||
|
||||
sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud,
|
||||
"stub_rx");
|
||||
sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud,
|
||||
"stub_tx");
|
||||
|
||||
spin_lock_irq(&sdev->ud.lock);
|
||||
sdev->ud.status = SDEV_ST_USED;
|
||||
spin_unlock_irq(&sdev->ud.lock);
|
||||
|
||||
} else {
|
||||
dev_info(dev, "stub down\n");
|
||||
|
||||
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);
|
||||
|
||||
static int stub_add_files(struct device *dev)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = device_create_file(dev, &dev_attr_usbip_status);
|
||||
if (err)
|
||||
goto err_status;
|
||||
|
||||
err = device_create_file(dev, &dev_attr_usbip_sockfd);
|
||||
if (err)
|
||||
goto err_sockfd;
|
||||
|
||||
err = device_create_file(dev, &dev_attr_usbip_debug);
|
||||
if (err)
|
||||
goto err_debug;
|
||||
|
||||
return 0;
|
||||
|
||||
err_debug:
|
||||
device_remove_file(dev, &dev_attr_usbip_sockfd);
|
||||
err_sockfd:
|
||||
device_remove_file(dev, &dev_attr_usbip_status);
|
||||
err_status:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void stub_remove_files(struct device *dev)
|
||||
{
|
||||
device_remove_file(dev, &dev_attr_usbip_status);
|
||||
device_remove_file(dev, &dev_attr_usbip_sockfd);
|
||||
device_remove_file(dev, &dev_attr_usbip_debug);
|
||||
}
|
||||
|
||||
static void stub_shutdown_connection(struct usbip_device *ud)
|
||||
{
|
||||
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
|
||||
|
||||
/*
|
||||
* When removing an exported device, kernel panic sometimes occurred
|
||||
* and then EIP was sk_wait_data of stub_rx thread. Is this because
|
||||
* sk_wait_data returned though stub_rx thread was already finished by
|
||||
* step 1?
|
||||
*/
|
||||
if (ud->tcp_socket) {
|
||||
dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n",
|
||||
ud->tcp_socket);
|
||||
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
|
||||
}
|
||||
|
||||
/* 1. stop threads */
|
||||
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 that usbip_xmit does
|
||||
* not touch NULL socket.
|
||||
*/
|
||||
if (ud->tcp_socket) {
|
||||
sockfd_put(ud->tcp_socket);
|
||||
ud->tcp_socket = NULL;
|
||||
}
|
||||
|
||||
/* 3. free used data */
|
||||
stub_device_cleanup_urbs(sdev);
|
||||
|
||||
/* 4. free stub_unlink */
|
||||
{
|
||||
unsigned long flags;
|
||||
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_del(&unlink->list);
|
||||
kfree(unlink);
|
||||
}
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void stub_device_reset(struct usbip_device *ud)
|
||||
{
|
||||
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
|
||||
struct usb_device *udev = sdev->udev;
|
||||
int ret;
|
||||
|
||||
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_irq(&ud->lock);
|
||||
ud->status = SDEV_ST_ERROR;
|
||||
spin_unlock_irq(&ud->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* try to reset the device */
|
||||
ret = usb_reset_device(udev);
|
||||
usb_unlock_device(udev);
|
||||
|
||||
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_irq(&ud->lock);
|
||||
}
|
||||
|
||||
static void stub_device_unusable(struct usbip_device *ud)
|
||||
{
|
||||
spin_lock_irq(&ud->lock);
|
||||
ud->status = SDEV_ST_ERROR;
|
||||
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_device *udev)
|
||||
{
|
||||
struct stub_device *sdev;
|
||||
int busnum = udev->bus->busnum;
|
||||
int devnum = udev->devnum;
|
||||
|
||||
dev_dbg(&udev->dev, "allocating stub device");
|
||||
|
||||
/* yes, it's a new device */
|
||||
sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
|
||||
if (!sdev)
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
sdev->ud.side = USBIP_STUB;
|
||||
sdev->ud.status = SDEV_ST_AVAILABLE;
|
||||
spin_lock_init(&sdev->ud.lock);
|
||||
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);
|
||||
spin_lock_init(&sdev->priv_lock);
|
||||
|
||||
init_waitqueue_head(&sdev->tx_waitq);
|
||||
|
||||
sdev->ud.eh_ops.shutdown = stub_shutdown_connection;
|
||||
sdev->ud.eh_ops.reset = stub_device_reset;
|
||||
sdev->ud.eh_ops.unusable = stub_device_unusable;
|
||||
|
||||
usbip_start_eh(&sdev->ud);
|
||||
|
||||
dev_dbg(&udev->dev, "register new device\n");
|
||||
|
||||
return sdev;
|
||||
}
|
||||
|
||||
static void stub_device_free(struct stub_device *sdev)
|
||||
{
|
||||
kfree(sdev);
|
||||
}
|
||||
|
||||
static int stub_probe(struct usb_device *udev)
|
||||
{
|
||||
struct stub_device *sdev = NULL;
|
||||
const char *udev_busid = dev_name(&udev->dev);
|
||||
int err = 0;
|
||||
struct bus_id_priv *busid_priv;
|
||||
int rc;
|
||||
|
||||
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(&udev->dev,
|
||||
"%s is not in match_busid table... skip!\n",
|
||||
udev_busid);
|
||||
|
||||
/*
|
||||
* Return value should be ENODEV or ENOXIO to continue trying
|
||||
* other matched drivers by the driver core.
|
||||
* See driver_probe_device() in driver/base/dd.c
|
||||
*/
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
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")) {
|
||||
dev_dbg(&udev->dev,
|
||||
"%s is attached on vhci_hcd... skip!\n",
|
||||
udev_busid);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* ok, this is my device */
|
||||
sdev = stub_device_alloc(udev);
|
||||
if (!sdev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_info(&udev->dev,
|
||||
"usbip-host: register new device (bus %u dev %u)\n",
|
||||
udev->bus->busnum, udev->devnum);
|
||||
|
||||
busid_priv->shutdown_busid = 0;
|
||||
|
||||
/* 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(&udev->dev);
|
||||
if (err) {
|
||||
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);
|
||||
return err;
|
||||
}
|
||||
busid_priv->status = STUB_BUSID_ALLOC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shutdown_busid(struct bus_id_priv *busid_priv)
|
||||
{
|
||||
if (busid_priv->sdev && !busid_priv->shutdown_busid) {
|
||||
busid_priv->shutdown_busid = 1;
|
||||
usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED);
|
||||
|
||||
/* 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_device *udev)
|
||||
{
|
||||
struct stub_device *sdev;
|
||||
const char *udev_busid = dev_name(&udev->dev);
|
||||
struct bus_id_priv *busid_priv;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&udev->dev, "Enter\n");
|
||||
|
||||
busid_priv = get_busid_priv(udev_busid);
|
||||
if (!busid_priv) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
sdev = dev_get_drvdata(&udev->dev);
|
||||
|
||||
/* get stub_device */
|
||||
if (!sdev) {
|
||||
dev_err(&udev->dev, "could not get device");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&udev->dev, NULL);
|
||||
|
||||
/*
|
||||
* NOTE: rx/tx threads are invoked for each usb_device.
|
||||
*/
|
||||
stub_remove_files(&udev->dev);
|
||||
|
||||
/* 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 usb reset is called from event handler */
|
||||
if (busid_priv->sdev->ud.eh == current)
|
||||
return;
|
||||
|
||||
/* shutdown the current connection */
|
||||
shutdown_busid(busid_priv);
|
||||
|
||||
usb_put_dev(sdev->udev);
|
||||
|
||||
/* free sdev */
|
||||
busid_priv->sdev = NULL;
|
||||
stub_device_free(sdev);
|
||||
|
||||
if (busid_priv->status == STUB_BUSID_ALLOC) {
|
||||
busid_priv->status = STUB_BUSID_ADDED;
|
||||
} else {
|
||||
busid_priv->status = STUB_BUSID_OTHER;
|
||||
del_match_busid((char *)udev_busid);
|
||||
}
|
||||
}
|
||||
|
||||
#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,
|
||||
};
|
335
drivers/usb/usbip/stub_main.c
Normal file
335
drivers/usb/usbip/stub_main.c
Normal file
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "usbip_common.h"
|
||||
#include "stub.h"
|
||||
|
||||
#define DRIVER_AUTHOR "Takahiro Hirofuchi"
|
||||
#define DRIVER_DESC "USB/IP Host Driver"
|
||||
|
||||
struct kmem_cache *stub_priv_cache;
|
||||
/*
|
||||
* 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
|
||||
* remote host.
|
||||
*/
|
||||
#define MAX_BUSID 16
|
||||
static struct bus_id_priv busid_table[MAX_BUSID];
|
||||
static spinlock_t busid_table_lock;
|
||||
|
||||
static void init_busid_table(void)
|
||||
{
|
||||
/*
|
||||
* This also sets the bus_table[i].status to
|
||||
* STUB_BUSID_OTHER, which is 0.
|
||||
*/
|
||||
memset(busid_table, 0, sizeof(busid_table));
|
||||
|
||||
spin_lock_init(&busid_table_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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)) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
struct bus_id_priv *get_busid_priv(const char *busid)
|
||||
{
|
||||
int idx;
|
||||
struct bus_id_priv *bid = NULL;
|
||||
|
||||
spin_lock(&busid_table_lock);
|
||||
idx = get_busid_idx(busid);
|
||||
if (idx >= 0)
|
||||
bid = &(busid_table[idx]);
|
||||
spin_unlock(&busid_table_lock);
|
||||
|
||||
return bid;
|
||||
}
|
||||
|
||||
static int add_match_busid(char *busid)
|
||||
{
|
||||
int i;
|
||||
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]) {
|
||||
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
|
||||
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
|
||||
(busid_table[i].status != STUB_BUSID_REMOV))
|
||||
busid_table[i].status = STUB_BUSID_ADDED;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&busid_table_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int del_match_busid(char *busid)
|
||||
{
|
||||
int idx;
|
||||
int ret = -1;
|
||||
|
||||
spin_lock(&busid_table_lock);
|
||||
idx = get_busid_idx(busid);
|
||||
if (idx < 0)
|
||||
goto out;
|
||||
|
||||
/* 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 ret;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
return out - buf;
|
||||
}
|
||||
|
||||
static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int len;
|
||||
char busid[BUSID_SIZE];
|
||||
|
||||
if (count < 5)
|
||||
return -EINVAL;
|
||||
|
||||
/* busid needs to include \0 termination */
|
||||
len = strlcpy(busid, buf + 4, BUSID_SIZE);
|
||||
if (sizeof(busid) <= len)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strncmp(buf, "add ", 4)) {
|
||||
if (add_match_busid(busid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("add busid %s\n", busid);
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!strncmp(buf, "del ", 4)) {
|
||||
if (del_match_busid(busid) < 0)
|
||||
return -ENODEV;
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
static DRIVER_ATTR_WO(rebind);
|
||||
|
||||
static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
|
||||
{
|
||||
struct stub_priv *priv, *tmp;
|
||||
|
||||
list_for_each_entry_safe(priv, tmp, listhead, list) {
|
||||
list_del(&priv->list);
|
||||
return priv;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct stub_priv *priv;
|
||||
|
||||
spin_lock_irqsave(&sdev->priv_lock, flags);
|
||||
|
||||
priv = stub_priv_pop_from_listhead(&sdev->priv_init);
|
||||
if (priv)
|
||||
goto done;
|
||||
|
||||
priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
|
||||
if (priv)
|
||||
goto done;
|
||||
|
||||
priv = stub_priv_pop_from_listhead(&sdev->priv_free);
|
||||
|
||||
done:
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
|
||||
return priv;
|
||||
}
|
||||
|
||||
void stub_device_cleanup_urbs(struct stub_device *sdev)
|
||||
{
|
||||
struct stub_priv *priv;
|
||||
struct urb *urb;
|
||||
|
||||
dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev);
|
||||
|
||||
while ((priv = stub_priv_pop(sdev))) {
|
||||
urb = priv->urb;
|
||||
dev_dbg(&sdev->udev->dev, "free urb %p\n", urb);
|
||||
usb_kill_urb(urb);
|
||||
|
||||
kmem_cache_free(stub_priv_cache, priv);
|
||||
|
||||
kfree(urb->transfer_buffer);
|
||||
kfree(urb->setup_packet);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init usbip_host_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
init_busid_table();
|
||||
|
||||
stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN);
|
||||
if (!stub_priv_cache) {
|
||||
pr_err("kmem_cache_create failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = usb_register_device_driver(&stub_driver, THIS_MODULE);
|
||||
if (ret) {
|
||||
pr_err("usb_register failed %d\n", ret);
|
||||
goto err_usb_register;
|
||||
}
|
||||
|
||||
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) {
|
||||
pr_err("driver_create_file failed\n");
|
||||
goto err_create_file;
|
||||
}
|
||||
|
||||
pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
|
||||
return ret;
|
||||
|
||||
err_create_file:
|
||||
usb_deregister_device_driver(&stub_driver);
|
||||
err_usb_register:
|
||||
kmem_cache_destroy(stub_priv_cache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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_device_driver(&stub_driver);
|
||||
|
||||
kmem_cache_destroy(stub_priv_cache);
|
||||
}
|
||||
|
||||
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);
|
594
drivers/usb/usbip/stub_rx.c
Normal file
594
drivers/usb/usbip/stub_rx.c
Normal file
|
@ -0,0 +1,594 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
|
||||
#include "usbip_common.h"
|
||||
#include "stub.h"
|
||||
|
||||
static int is_clear_halt_cmd(struct urb *urb)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
|
||||
req = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
|
||||
return (req->bRequest == USB_REQ_CLEAR_FEATURE) &&
|
||||
(req->bRequestType == USB_RECIP_ENDPOINT) &&
|
||||
(req->wValue == USB_ENDPOINT_HALT);
|
||||
}
|
||||
|
||||
static int is_set_interface_cmd(struct urb *urb)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
|
||||
req = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
|
||||
return (req->bRequest == USB_REQ_SET_INTERFACE) &&
|
||||
(req->bRequestType == USB_RECIP_INTERFACE);
|
||||
}
|
||||
|
||||
static int is_set_configuration_cmd(struct urb *urb)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
|
||||
req = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
|
||||
return (req->bRequest == USB_REQ_SET_CONFIGURATION) &&
|
||||
(req->bRequestType == USB_RECIP_DEVICE);
|
||||
}
|
||||
|
||||
static int is_reset_device_cmd(struct urb *urb)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
__u16 value;
|
||||
__u16 index;
|
||||
|
||||
req = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
value = le16_to_cpu(req->wValue);
|
||||
index = le16_to_cpu(req->wIndex);
|
||||
|
||||
if ((req->bRequest == USB_REQ_SET_FEATURE) &&
|
||||
(req->bRequestType == USB_RT_PORT) &&
|
||||
(value == USB_PORT_FEAT_RESET)) {
|
||||
usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tweak_clear_halt_cmd(struct urb *urb)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
int target_endp;
|
||||
int target_dir;
|
||||
int target_pipe;
|
||||
int ret;
|
||||
|
||||
req = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
|
||||
/*
|
||||
* The stalled endpoint is specified in the wIndex value. The endpoint
|
||||
* of the urb is the target of this clear_halt request (i.e., control
|
||||
* endpoint).
|
||||
*/
|
||||
target_endp = le16_to_cpu(req->wIndex) & 0x000f;
|
||||
|
||||
/* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */
|
||||
target_dir = le16_to_cpu(req->wIndex) & 0x0080;
|
||||
|
||||
if (target_dir)
|
||||
target_pipe = usb_rcvctrlpipe(urb->dev, target_endp);
|
||||
else
|
||||
target_pipe = usb_sndctrlpipe(urb->dev, target_endp);
|
||||
|
||||
ret = usb_clear_halt(urb->dev, target_pipe);
|
||||
if (ret < 0)
|
||||
dev_err(&urb->dev->dev,
|
||||
"usb_clear_halt error: devnum %d endp %d ret %d\n",
|
||||
urb->dev->devnum, target_endp, ret);
|
||||
else
|
||||
dev_info(&urb->dev->dev,
|
||||
"usb_clear_halt done: devnum %d endp %d\n",
|
||||
urb->dev->devnum, target_endp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tweak_set_interface_cmd(struct urb *urb)
|
||||
{
|
||||
struct usb_ctrlrequest *req;
|
||||
__u16 alternate;
|
||||
__u16 interface;
|
||||
int ret;
|
||||
|
||||
req = (struct usb_ctrlrequest *) urb->setup_packet;
|
||||
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);
|
||||
|
||||
ret = usb_set_interface(urb->dev, interface, alternate);
|
||||
if (ret < 0)
|
||||
dev_err(&urb->dev->dev,
|
||||
"usb_set_interface error: inf %u alt %u ret %d\n",
|
||||
interface, alternate, ret);
|
||||
else
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int tweak_reset_device_cmd(struct urb *urb)
|
||||
{
|
||||
struct stub_priv *priv = (struct stub_priv *) urb->context;
|
||||
struct stub_device *sdev = priv->sdev;
|
||||
|
||||
dev_info(&urb->dev->dev, "usb_queue_reset_device\n");
|
||||
|
||||
/*
|
||||
* With the implementation of pre_reset and post_reset the driver no
|
||||
* longer unbinds. This allows the use of synchronous reset.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* clear_halt, set_interface, and set_configuration require special tricks.
|
||||
*/
|
||||
static void tweak_special_requests(struct urb *urb)
|
||||
{
|
||||
if (!urb || !urb->setup_packet)
|
||||
return;
|
||||
|
||||
if (usb_pipetype(urb->pipe) != PIPE_CONTROL)
|
||||
return;
|
||||
|
||||
if (is_clear_halt_cmd(urb))
|
||||
/* tweak clear_halt */
|
||||
tweak_clear_halt_cmd(urb);
|
||||
|
||||
else if (is_set_interface_cmd(urb))
|
||||
/* tweak set_interface */
|
||||
tweak_set_interface_cmd(urb);
|
||||
|
||||
else if (is_set_configuration_cmd(urb))
|
||||
/* tweak set_configuration */
|
||||
tweak_set_configuration_cmd(urb);
|
||||
|
||||
else if (is_reset_device_cmd(urb))
|
||||
tweak_reset_device_cmd(urb);
|
||||
else
|
||||
usbip_dbg_stub_rx("no need to tweak\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb().
|
||||
* By unlinking the urb asynchronously, stub_rx can continuously
|
||||
* process coming urbs. Even if the urb is unlinked, its completion
|
||||
* handler will be called and stub_tx will send a return pdu.
|
||||
*
|
||||
* See also comments about unlinking strategy in vhci_hcd.c.
|
||||
*/
|
||||
static int stub_recv_cmd_unlink(struct stub_device *sdev,
|
||||
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)
|
||||
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);
|
||||
|
||||
/*
|
||||
* The urb of the unlink target is not found in priv_init queue. It was
|
||||
* already completed and its results is/was going to be sent by a
|
||||
* CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only
|
||||
* return the completeness of this unlink request to vhci_hcd.
|
||||
*/
|
||||
stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0);
|
||||
|
||||
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_irq(&ud->lock);
|
||||
if (ud->status == SDEV_ST_USED) {
|
||||
/* A request is valid. */
|
||||
valid = 1;
|
||||
}
|
||||
spin_unlock_irq(&ud->lock);
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
static struct stub_priv *stub_priv_alloc(struct stub_device *sdev,
|
||||
struct usbip_header *pdu)
|
||||
{
|
||||
struct stub_priv *priv;
|
||||
struct usbip_device *ud = &sdev->ud;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sdev->priv_lock, flags);
|
||||
|
||||
priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC);
|
||||
if (!priv) {
|
||||
dev_err(&sdev->interface->dev, "alloc stub_priv\n");
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
priv->seqnum = pdu->base.seqnum;
|
||||
priv->sdev = sdev;
|
||||
|
||||
/*
|
||||
* After a stub_priv is linked to a list_head,
|
||||
* our error handler can free allocated data.
|
||||
*/
|
||||
list_add_tail(&priv->list, &sdev->priv_init);
|
||||
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
|
||||
return priv;
|
||||
}
|
||||
|
||||
static int get_pipe(struct stub_device *sdev, int epnum, int dir)
|
||||
{
|
||||
struct usb_device *udev = sdev->udev;
|
||||
struct usb_host_endpoint *ep;
|
||||
struct usb_endpoint_descriptor *epd = NULL;
|
||||
|
||||
if (dir == USBIP_DIR_IN)
|
||||
ep = udev->ep_in[epnum & 0x7f];
|
||||
else
|
||||
ep = udev->ep_out[epnum & 0x7f];
|
||||
if (!ep) {
|
||||
dev_err(&sdev->interface->dev, "no such endpoint?, %d\n",
|
||||
epnum);
|
||||
BUG();
|
||||
}
|
||||
|
||||
epd = &ep->desc;
|
||||
if (usb_endpoint_xfer_control(epd)) {
|
||||
if (dir == USBIP_DIR_OUT)
|
||||
return usb_sndctrlpipe(udev, epnum);
|
||||
else
|
||||
return usb_rcvctrlpipe(udev, epnum);
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_bulk(epd)) {
|
||||
if (dir == USBIP_DIR_OUT)
|
||||
return usb_sndbulkpipe(udev, epnum);
|
||||
else
|
||||
return usb_rcvbulkpipe(udev, epnum);
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_int(epd)) {
|
||||
if (dir == USBIP_DIR_OUT)
|
||||
return usb_sndintpipe(udev, epnum);
|
||||
else
|
||||
return usb_rcvintpipe(udev, epnum);
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(epd)) {
|
||||
if (dir == USBIP_DIR_OUT)
|
||||
return usb_sndisocpipe(udev, epnum);
|
||||
else
|
||||
return usb_rcvisocpipe(udev, epnum);
|
||||
}
|
||||
|
||||
/* NOT REACHED */
|
||||
dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void masking_bogus_flags(struct urb *urb)
|
||||
{
|
||||
int xfertype;
|
||||
struct usb_device *dev;
|
||||
struct usb_host_endpoint *ep;
|
||||
int is_out;
|
||||
unsigned int allowed;
|
||||
|
||||
if (!urb || urb->hcpriv || !urb->complete)
|
||||
return;
|
||||
dev = urb->dev;
|
||||
if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
|
||||
return;
|
||||
|
||||
ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)
|
||||
[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;
|
||||
|
||||
if (!setup)
|
||||
return;
|
||||
is_out = !(setup->bRequestType & USB_DIR_IN) ||
|
||||
!setup->wLength;
|
||||
} else {
|
||||
is_out = usb_endpoint_dir_out(&ep->desc);
|
||||
}
|
||||
|
||||
/* enforce simple/standard policy */
|
||||
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT |
|
||||
URB_DIR_MASK | URB_FREE_BUFFER);
|
||||
switch (xfertype) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (is_out)
|
||||
allowed |= URB_ZERO_PACKET;
|
||||
/* FALLTHROUGH */
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
allowed |= URB_NO_FSBR; /* only affects UHCI */
|
||||
/* FALLTHROUGH */
|
||||
default: /* all non-iso endpoints */
|
||||
if (!is_out)
|
||||
allowed |= URB_SHORT_NOT_OK;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
allowed |= URB_ISO_ASAP;
|
||||
break;
|
||||
}
|
||||
urb->transfer_flags &= allowed;
|
||||
}
|
||||
|
||||
static void stub_recv_cmd_submit(struct stub_device *sdev,
|
||||
struct usbip_header *pdu)
|
||||
{
|
||||
int ret;
|
||||
struct stub_priv *priv;
|
||||
struct usbip_device *ud = &sdev->ud;
|
||||
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;
|
||||
|
||||
/* setup a urb */
|
||||
if (usb_pipeisoc(pipe))
|
||||
priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
|
||||
GFP_KERNEL);
|
||||
else
|
||||
priv->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
|
||||
if (!priv->urb) {
|
||||
dev_err(&sdev->interface->dev, "malloc urb\n");
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (!priv->urb->transfer_buffer) {
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy urb setup packet */
|
||||
priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8,
|
||||
GFP_KERNEL);
|
||||
if (!priv->urb->setup_packet) {
|
||||
dev_err(&sdev->interface->dev, "allocate setup_packet\n");
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set other members from the base header of pdu */
|
||||
priv->urb->context = (void *) priv;
|
||||
priv->urb->dev = udev;
|
||||
priv->urb->pipe = pipe;
|
||||
priv->urb->complete = stub_complete;
|
||||
|
||||
usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0);
|
||||
|
||||
|
||||
if (usbip_recv_xbuff(ud, priv->urb) < 0)
|
||||
return;
|
||||
|
||||
if (usbip_recv_iso(ud, priv->urb) < 0)
|
||||
return;
|
||||
|
||||
/* no need to submit an intercepted request, but harmless? */
|
||||
tweak_special_requests(priv->urb);
|
||||
|
||||
masking_bogus_flags(priv->urb);
|
||||
/* urb is now ready to submit */
|
||||
ret = usb_submit_urb(priv->urb, GFP_KERNEL);
|
||||
|
||||
if (ret == 0)
|
||||
usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
|
||||
pdu->base.seqnum);
|
||||
else {
|
||||
dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret);
|
||||
usbip_dump_header(pdu);
|
||||
usbip_dump_urb(priv->urb);
|
||||
|
||||
/*
|
||||
* Pessimistic.
|
||||
* This connection will be discarded.
|
||||
*/
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
|
||||
}
|
||||
|
||||
usbip_dbg_stub_rx("Leave\n");
|
||||
}
|
||||
|
||||
/* recv a pdu */
|
||||
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->udev->dev;
|
||||
|
||||
usbip_dbg_stub_rx("Enter\n");
|
||||
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
|
||||
/* 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);
|
||||
return;
|
||||
}
|
||||
|
||||
usbip_header_correct_endian(&pdu, 0);
|
||||
|
||||
if (usbip_dbg_flag_stub_rx)
|
||||
usbip_dump_header(&pdu);
|
||||
|
||||
if (!valid_request(sdev, &pdu)) {
|
||||
dev_err(dev, "recv invalid request\n");
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pdu.base.command) {
|
||||
case USBIP_CMD_UNLINK:
|
||||
stub_recv_cmd_unlink(sdev, &pdu);
|
||||
break;
|
||||
|
||||
case USBIP_CMD_SUBMIT:
|
||||
stub_recv_cmd_submit(sdev, &pdu);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* NOTREACHED */
|
||||
dev_err(dev, "unknown pdu\n");
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int stub_rx_loop(void *data)
|
||||
{
|
||||
struct usbip_device *ud = data;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (usbip_event_happened(ud))
|
||||
break;
|
||||
|
||||
stub_rx_pdu(ud);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
398
drivers/usb/usbip/stub_tx.c
Normal file
398
drivers/usb/usbip/stub_tx.c
Normal file
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
kfree(urb->setup_packet);
|
||||
kfree(urb->transfer_buffer);
|
||||
list_del(&priv->list);
|
||||
kmem_cache_free(stub_priv_cache, priv);
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
|
||||
/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */
|
||||
void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
|
||||
__u32 status)
|
||||
{
|
||||
struct stub_unlink *unlink;
|
||||
|
||||
unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC);
|
||||
if (!unlink) {
|
||||
usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC);
|
||||
return;
|
||||
}
|
||||
|
||||
unlink->seqnum = seqnum;
|
||||
unlink->status = status;
|
||||
|
||||
list_add_tail(&unlink->list, &sdev->unlink_tx);
|
||||
}
|
||||
|
||||
/**
|
||||
* stub_complete - completion handler of a usbip urb
|
||||
* @urb: pointer to the urb completed
|
||||
*
|
||||
* When a urb has completed, the USB core driver calls this function mostly in
|
||||
* the interrupt context. To return the result of a urb, the completed urb is
|
||||
* linked to the pending list of returning.
|
||||
*
|
||||
*/
|
||||
void stub_complete(struct urb *urb)
|
||||
{
|
||||
struct stub_priv *priv = (struct stub_priv *) urb->context;
|
||||
struct stub_device *sdev = priv->sdev;
|
||||
unsigned long flags;
|
||||
|
||||
usbip_dbg_stub_tx("complete! status %d\n", urb->status);
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* OK */
|
||||
break;
|
||||
case -ENOENT:
|
||||
dev_info(&urb->dev->dev,
|
||||
"stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
|
||||
return;
|
||||
case -ECONNRESET:
|
||||
dev_info(&urb->dev->dev,
|
||||
"unlinked by a call to usb_unlink_urb()\n");
|
||||
break;
|
||||
case -EPIPE:
|
||||
dev_info(&urb->dev->dev, "endpoint %d is stalled\n",
|
||||
usb_pipeendpoint(urb->pipe));
|
||||
break;
|
||||
case -ESHUTDOWN:
|
||||
dev_info(&urb->dev->dev, "device removed?\n");
|
||||
break;
|
||||
default:
|
||||
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 {
|
||||
list_move_tail(&priv->list, &sdev->priv_tx);
|
||||
}
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
|
||||
/* wake up tx_thread */
|
||||
wake_up(&sdev->tx_waitq);
|
||||
}
|
||||
|
||||
static inline void setup_base_pdu(struct usbip_header_basic *base,
|
||||
__u32 command, __u32 seqnum)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum);
|
||||
rpdu->u.ret_unlink.status = unlink->status;
|
||||
}
|
||||
|
||||
static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct stub_priv *priv, *tmp;
|
||||
|
||||
spin_lock_irqsave(&sdev->priv_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) {
|
||||
list_move_tail(&priv->list, &sdev->priv_free);
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
return priv;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int stub_send_ret_submit(struct stub_device *sdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct stub_priv *priv, *tmp;
|
||||
|
||||
struct msghdr msg;
|
||||
size_t txsize;
|
||||
|
||||
size_t total_size = 0;
|
||||
|
||||
while ((priv = dequeue_from_priv_tx(sdev)) != NULL) {
|
||||
int ret;
|
||||
struct urb *urb = priv->urb;
|
||||
struct usbip_header pdu_header;
|
||||
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));
|
||||
|
||||
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[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) &&
|
||||
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 */
|
||||
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
|
||||
ssize_t len = 0;
|
||||
|
||||
iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len);
|
||||
if (!iso_buffer) {
|
||||
usbip_event_add(&sdev->ud,
|
||||
SDEV_EVENT_ERROR_MALLOC);
|
||||
kfree(iov);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iov[iovnum].iov_base = iso_buffer;
|
||||
iov[iovnum].iov_len = len;
|
||||
txsize += len;
|
||||
iovnum++;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct stub_unlink *unlink, *tmp;
|
||||
|
||||
spin_lock_irqsave(&sdev->priv_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
|
||||
list_move_tail(&unlink->list, &sdev->unlink_free);
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
return unlink;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int stub_send_ret_unlink(struct stub_device *sdev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct stub_unlink *unlink, *tmp;
|
||||
|
||||
struct msghdr msg;
|
||||
struct kvec iov[1];
|
||||
size_t txsize;
|
||||
|
||||
size_t total_size = 0;
|
||||
|
||||
while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) {
|
||||
int ret;
|
||||
struct usbip_header pdu_header;
|
||||
|
||||
txsize = 0;
|
||||
memset(&pdu_header, 0, sizeof(pdu_header));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
|
||||
usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum);
|
||||
|
||||
/* 1. setup usbip_header */
|
||||
setup_ret_unlink_pdu(&pdu_header, unlink);
|
||||
usbip_header_correct_endian(&pdu_header, 1);
|
||||
|
||||
iov[0].iov_base = &pdu_header;
|
||||
iov[0].iov_len = sizeof(pdu_header);
|
||||
txsize += sizeof(pdu_header);
|
||||
|
||||
ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
|
||||
1, txsize);
|
||||
if (ret != txsize) {
|
||||
dev_err(&sdev->interface->dev,
|
||||
"sendmsg failed!, retval %d for %zd\n",
|
||||
ret, txsize);
|
||||
usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
|
||||
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) {
|
||||
list_del(&unlink->list);
|
||||
kfree(unlink);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sdev->priv_lock, flags);
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
int stub_tx_loop(void *data)
|
||||
{
|
||||
struct usbip_device *ud = data;
|
||||
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (usbip_event_happened(ud))
|
||||
break;
|
||||
|
||||
/*
|
||||
* send_ret_submit comes earlier than send_ret_unlink. stub_rx
|
||||
* looks at only priv_init queue. If the completion of a URB is
|
||||
* earlier than the receive of CMD_UNLINK, priv is moved to
|
||||
* priv_tx queue and stub_rx does not find the target priv. In
|
||||
* this case, vhci_rx receives the result of the submit request
|
||||
* and then receives the result of the unlink request. The
|
||||
* result of the submit is given back to the usbcore as the
|
||||
* completion of the unlink request. The request of the
|
||||
* unlink is ignored. This is ok because a driver who calls
|
||||
* usb_unlink_urb() understands the unlink was too late by
|
||||
* getting the status of the given-backed URB which has the
|
||||
* status of usb_submit_urb().
|
||||
*/
|
||||
if (stub_send_ret_submit(sdev) < 0)
|
||||
break;
|
||||
|
||||
if (stub_send_ret_unlink(sdev) < 0)
|
||||
break;
|
||||
|
||||
wait_event_interruptible(sdev->tx_waitq,
|
||||
(!list_empty(&sdev->priv_tx) ||
|
||||
!list_empty(&sdev->unlink_tx) ||
|
||||
kthread_should_stop()));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
776
drivers/usb/usbip/usbip_common.c
Normal file
776
drivers/usb/usbip/usbip_common.c
Normal file
|
@ -0,0 +1,776 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "usbip_common.h"
|
||||
|
||||
#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>"
|
||||
#define DRIVER_DESC "USB/IP Core"
|
||||
|
||||
#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 usbip_debug_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%lx\n", usbip_debug_flag);
|
||||
}
|
||||
|
||||
static ssize_t usbip_debug_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (sscanf(buf, "%lx", &usbip_debug_flag) != 1)
|
||||
return -EINVAL;
|
||||
return count;
|
||||
}
|
||||
DEVICE_ATTR_RW(usbip_debug);
|
||||
|
||||
static void usbip_dump_buffer(char *buff, int bufflen)
|
||||
{
|
||||
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);
|
||||
|
||||
pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT");
|
||||
|
||||
switch (type) {
|
||||
case PIPE_ISOCHRONOUS:
|
||||
pr_debug("ISO\n");
|
||||
break;
|
||||
case PIPE_INTERRUPT:
|
||||
pr_debug("INT\n");
|
||||
break;
|
||||
case PIPE_CONTROL:
|
||||
pr_debug("CTRL\n");
|
||||
break;
|
||||
case PIPE_BULK:
|
||||
pr_debug("BULK\n");
|
||||
break;
|
||||
default:
|
||||
pr_debug("ERR\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbip_dump_usb_device(struct usb_device *udev)
|
||||
{
|
||||
struct device *dev = &udev->dev;
|
||||
int i;
|
||||
|
||||
dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)",
|
||||
udev->devnum, udev->devpath, usb_speed_string(udev->speed));
|
||||
|
||||
pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport);
|
||||
|
||||
dev_dbg(dev, " ");
|
||||
for (i = 0; i < 16; i++)
|
||||
pr_debug(" %2u", i);
|
||||
pr_debug("\n");
|
||||
|
||||
dev_dbg(dev, " toggle0(IN) :");
|
||||
for (i = 0; i < 16; i++)
|
||||
pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0);
|
||||
pr_debug("\n");
|
||||
|
||||
dev_dbg(dev, " toggle1(OUT):");
|
||||
for (i = 0; i < 16; i++)
|
||||
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])
|
||||
pr_debug(" %2u",
|
||||
le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize));
|
||||
}
|
||||
pr_debug("\n");
|
||||
|
||||
dev_dbg(dev, " epmaxp_out :");
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (udev->ep_out[i])
|
||||
pr_debug(" %2u",
|
||||
le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize));
|
||||
}
|
||||
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,
|
||||
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\n", udev->maxchild);
|
||||
}
|
||||
|
||||
static void usbip_dump_request_type(__u8 rt)
|
||||
{
|
||||
switch (rt & USB_RECIP_MASK) {
|
||||
case USB_RECIP_DEVICE:
|
||||
pr_debug("DEVICE");
|
||||
break;
|
||||
case USB_RECIP_INTERFACE:
|
||||
pr_debug("INTERF");
|
||||
break;
|
||||
case USB_RECIP_ENDPOINT:
|
||||
pr_debug("ENDPOI");
|
||||
break;
|
||||
case USB_RECIP_OTHER:
|
||||
pr_debug("OTHER ");
|
||||
break;
|
||||
default:
|
||||
pr_debug("------");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd)
|
||||
{
|
||||
if (!cmd) {
|
||||
pr_debug(" : null pointer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
pr_debug("STANDARD ");
|
||||
switch (cmd->bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
pr_debug("GET_STATUS\n");
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
pr_debug("CLEAR_FEAT\n");
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
pr_debug("SET_FEAT\n");
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
pr_debug("SET_ADDRRS\n");
|
||||
break;
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
pr_debug("GET_DESCRI\n");
|
||||
break;
|
||||
case USB_REQ_SET_DESCRIPTOR:
|
||||
pr_debug("SET_DESCRI\n");
|
||||
break;
|
||||
case USB_REQ_GET_CONFIGURATION:
|
||||
pr_debug("GET_CONFIG\n");
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
pr_debug("SET_CONFIG\n");
|
||||
break;
|
||||
case USB_REQ_GET_INTERFACE:
|
||||
pr_debug("GET_INTERF\n");
|
||||
break;
|
||||
case USB_REQ_SET_INTERFACE:
|
||||
pr_debug("SET_INTERF\n");
|
||||
break;
|
||||
case USB_REQ_SYNCH_FRAME:
|
||||
pr_debug("SYNC_FRAME\n");
|
||||
break;
|
||||
default:
|
||||
pr_debug("REQ(%02X)\n", cmd->bRequest);
|
||||
break;
|
||||
}
|
||||
usbip_dump_request_type(cmd->bRequestType);
|
||||
} 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)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
if (!urb) {
|
||||
pr_debug("urb: null pointer!!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!urb->dev) {
|
||||
pr_debug("urb->dev: null pointer!!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev = &urb->dev->dev;
|
||||
|
||||
dev_dbg(dev, " urb :%p\n", urb);
|
||||
dev_dbg(dev, " dev :%p\n", urb->dev);
|
||||
|
||||
usbip_dump_usb_device(urb->dev);
|
||||
|
||||
dev_dbg(dev, " pipe :%08x ", urb->pipe);
|
||||
|
||||
usbip_dump_pipe(urb->pipe);
|
||||
|
||||
dev_dbg(dev, " status :%d\n", urb->status);
|
||||
dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags);
|
||||
dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer);
|
||||
dev_dbg(dev, " transfer_buffer_length:%d\n",
|
||||
urb->transfer_buffer_length);
|
||||
dev_dbg(dev, " actual_length :%d\n", urb->actual_length);
|
||||
dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet);
|
||||
|
||||
if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL)
|
||||
usbip_dump_usb_ctrlrequest(
|
||||
(struct usb_ctrlrequest *)urb->setup_packet);
|
||||
|
||||
dev_dbg(dev, " start_frame :%d\n", urb->start_frame);
|
||||
dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets);
|
||||
dev_dbg(dev, " interval :%d\n", urb->interval);
|
||||
dev_dbg(dev, " error_count :%d\n", urb->error_count);
|
||||
dev_dbg(dev, " context :%p\n", urb->context);
|
||||
dev_dbg(dev, " complete :%p\n", urb->complete);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_dump_urb);
|
||||
|
||||
void usbip_dump_header(struct usbip_header *pdu)
|
||||
{
|
||||
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:
|
||||
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:
|
||||
pr_debug("USBIP_CMD_UNLINK: seq %u\n",
|
||||
pdu->u.cmd_unlink.seqnum);
|
||||
break;
|
||||
case USBIP_RET_SUBMIT:
|
||||
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:
|
||||
pr_debug("USBIP_RET_UNLINK: status %d\n",
|
||||
pdu->u.ret_unlink.status);
|
||||
break;
|
||||
default:
|
||||
/* NOT REACHED */
|
||||
pr_err("unknown command\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_dump_header);
|
||||
|
||||
/* Receive data over TCP/IP. */
|
||||
int usbip_recv(struct socket *sock, void *buf, int size)
|
||||
{
|
||||
int result;
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
int total = 0;
|
||||
|
||||
/* for blocks of if (usbip_dbg_flag_xmit) */
|
||||
char *bp = buf;
|
||||
int osize = size;
|
||||
|
||||
usbip_dbg_xmit("enter\n");
|
||||
|
||||
if (!sock || !buf || !size) {
|
||||
pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
|
||||
size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
do {
|
||||
sock->sk->sk_allocation = GFP_NOIO;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = size;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = MSG_NOSIGNAL;
|
||||
|
||||
result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
|
||||
if (result <= 0) {
|
||||
pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
|
||||
sock, buf, size, result, total);
|
||||
goto err;
|
||||
}
|
||||
|
||||
size -= result;
|
||||
buf += result;
|
||||
total += result;
|
||||
} while (size > 0);
|
||||
|
||||
if (usbip_dbg_flag_xmit) {
|
||||
if (!in_interrupt())
|
||||
pr_debug("%-10s:", current->comm);
|
||||
else
|
||||
pr_debug("interrupt :");
|
||||
|
||||
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;
|
||||
|
||||
err:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_recv);
|
||||
|
||||
/* there may be more cases to tweak the flags. */
|
||||
static unsigned int tweak_transfer_flags(unsigned int flags)
|
||||
{
|
||||
flags &= ~URB_NO_TRANSFER_DMA_MAP;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb,
|
||||
int pack)
|
||||
{
|
||||
struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit;
|
||||
|
||||
/*
|
||||
* Some members are not still implemented in usbip. I hope this issue
|
||||
* will be discussed when usbip is ported to other operating systems.
|
||||
*/
|
||||
if (pack) {
|
||||
spdu->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 {
|
||||
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;
|
||||
urb->interval = spdu->interval;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb,
|
||||
int pack)
|
||||
{
|
||||
struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit;
|
||||
|
||||
if (pack) {
|
||||
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 {
|
||||
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)
|
||||
{
|
||||
switch (cmd) {
|
||||
case USBIP_CMD_SUBMIT:
|
||||
usbip_pack_cmd_submit(pdu, urb, pack);
|
||||
break;
|
||||
case USBIP_RET_SUBMIT:
|
||||
usbip_pack_ret_submit(pdu, urb, pack);
|
||||
break;
|
||||
default:
|
||||
/* 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) {
|
||||
base->command = cpu_to_be32(base->command);
|
||||
base->seqnum = cpu_to_be32(base->seqnum);
|
||||
base->devid = cpu_to_be32(base->devid);
|
||||
base->direction = cpu_to_be32(base->direction);
|
||||
base->ep = cpu_to_be32(base->ep);
|
||||
} else {
|
||||
base->command = be32_to_cpu(base->command);
|
||||
base->seqnum = be32_to_cpu(base->seqnum);
|
||||
base->devid = be32_to_cpu(base->devid);
|
||||
base->direction = be32_to_cpu(base->direction);
|
||||
base->ep = be32_to_cpu(base->ep);
|
||||
}
|
||||
}
|
||||
|
||||
static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu,
|
||||
int send)
|
||||
{
|
||||
if (send) {
|
||||
pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags);
|
||||
|
||||
cpu_to_be32s(&pdu->transfer_buffer_length);
|
||||
cpu_to_be32s(&pdu->start_frame);
|
||||
cpu_to_be32s(&pdu->number_of_packets);
|
||||
cpu_to_be32s(&pdu->interval);
|
||||
} else {
|
||||
pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags);
|
||||
|
||||
be32_to_cpus(&pdu->transfer_buffer_length);
|
||||
be32_to_cpus(&pdu->start_frame);
|
||||
be32_to_cpus(&pdu->number_of_packets);
|
||||
be32_to_cpus(&pdu->interval);
|
||||
}
|
||||
}
|
||||
|
||||
static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu,
|
||||
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)
|
||||
{
|
||||
if (send)
|
||||
pdu->seqnum = cpu_to_be32(pdu->seqnum);
|
||||
else
|
||||
pdu->seqnum = be32_to_cpu(pdu->seqnum);
|
||||
}
|
||||
|
||||
static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu,
|
||||
int send)
|
||||
{
|
||||
if (send)
|
||||
cpu_to_be32s(&pdu->status);
|
||||
else
|
||||
be32_to_cpus(&pdu->status);
|
||||
}
|
||||
|
||||
void usbip_header_correct_endian(struct usbip_header *pdu, int send)
|
||||
{
|
||||
__u32 cmd = 0;
|
||||
|
||||
if (send)
|
||||
cmd = pdu->base.command;
|
||||
|
||||
correct_endian_basic(&pdu->base, send);
|
||||
|
||||
if (!send)
|
||||
cmd = pdu->base.command;
|
||||
|
||||
switch (cmd) {
|
||||
case USBIP_CMD_SUBMIT:
|
||||
correct_endian_cmd_submit(&pdu->u.cmd_submit, send);
|
||||
break;
|
||||
case USBIP_RET_SUBMIT:
|
||||
correct_endian_ret_submit(&pdu->u.ret_submit, send);
|
||||
break;
|
||||
case USBIP_CMD_UNLINK:
|
||||
correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send);
|
||||
break;
|
||||
case USBIP_RET_UNLINK:
|
||||
correct_endian_ret_unlink(&pdu->u.ret_unlink, send);
|
||||
break;
|
||||
default:
|
||||
/* NOT REACHED */
|
||||
pr_err("unknown command\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_header_correct_endian);
|
||||
|
||||
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) {
|
||||
iso->offset = cpu_to_be32(iso->offset);
|
||||
iso->length = cpu_to_be32(iso->length);
|
||||
iso->status = cpu_to_be32(iso->status);
|
||||
iso->actual_length = cpu_to_be32(iso->actual_length);
|
||||
} else {
|
||||
iso->offset = be32_to_cpu(iso->offset);
|
||||
iso->length = be32_to_cpu(iso->length);
|
||||
iso->status = be32_to_cpu(iso->status);
|
||||
iso->actual_length = be32_to_cpu(iso->actual_length);
|
||||
}
|
||||
}
|
||||
|
||||
static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso,
|
||||
struct usb_iso_packet_descriptor *uiso, int pack)
|
||||
{
|
||||
if (pack) {
|
||||
iso->offset = uiso->offset;
|
||||
iso->length = uiso->length;
|
||||
iso->status = uiso->status;
|
||||
iso->actual_length = uiso->actual_length;
|
||||
} else {
|
||||
uiso->offset = iso->offset;
|
||||
uiso->length = iso->length;
|
||||
uiso->status = iso->status;
|
||||
uiso->actual_length = iso->actual_length;
|
||||
}
|
||||
}
|
||||
|
||||
/* must free buffer */
|
||||
struct usbip_iso_packet_descriptor*
|
||||
usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen)
|
||||
{
|
||||
struct usbip_iso_packet_descriptor *iso;
|
||||
int np = urb->number_of_packets;
|
||||
ssize_t size = np * sizeof(*iso);
|
||||
int i;
|
||||
|
||||
iso = kzalloc(size, GFP_KERNEL);
|
||||
if (!iso)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < np; i++) {
|
||||
usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1);
|
||||
usbip_iso_packet_correct_endian(&iso[i], 1);
|
||||
}
|
||||
|
||||
*bufflen = size;
|
||||
|
||||
return iso;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu);
|
||||
|
||||
/* some members of urb must be substituted before. */
|
||||
int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
|
||||
{
|
||||
void *buff;
|
||||
struct usbip_iso_packet_descriptor *iso;
|
||||
int np = urb->number_of_packets;
|
||||
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)
|
||||
return 0;
|
||||
|
||||
buff = kzalloc(size, GFP_KERNEL);
|
||||
if (!buff)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = usbip_recv(ud->tcp_socket, buff, size);
|
||||
if (ret != size) {
|
||||
dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n",
|
||||
ret);
|
||||
kfree(buff);
|
||||
|
||||
if (ud->side == USBIP_STUB)
|
||||
usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
|
||||
else
|
||||
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
|
||||
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
iso = (struct usbip_iso_packet_descriptor *) buff;
|
||||
for (i = 0; i < np; i++) {
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
int size;
|
||||
|
||||
if (ud->side == USBIP_STUB) {
|
||||
/* the direction of urb must be OUT. */
|
||||
if (usb_pipein(urb->pipe))
|
||||
return 0;
|
||||
|
||||
size = urb->transfer_buffer_length;
|
||||
} else {
|
||||
/* the direction of urb must be IN. */
|
||||
if (usb_pipeout(urb->pipe))
|
||||
return 0;
|
||||
|
||||
size = urb->actual_length;
|
||||
}
|
||||
|
||||
/* no need to recv xbuff */
|
||||
if (!(size > 0))
|
||||
return 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) {
|
||||
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_xbuff);
|
||||
|
||||
static int __init usbip_core_init(void)
|
||||
{
|
||||
pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit usbip_core_exit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
335
drivers/usb/usbip/usbip_common.h
Normal file
335
drivers/usb/usbip/usbip_common.h
Normal file
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifndef __USBIP_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 <linux/wait.h>
|
||||
#include <uapi/linux/usbip.h>
|
||||
|
||||
#define USBIP_VERSION "1.0.0"
|
||||
|
||||
#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),
|
||||
usbip_debug_sysfs = (1 << 1),
|
||||
usbip_debug_urb = (1 << 2),
|
||||
usbip_debug_eh = (1 << 3),
|
||||
|
||||
usbip_debug_stub_cmp = (1 << 8),
|
||||
usbip_debug_stub_dev = (1 << 9),
|
||||
usbip_debug_stub_rx = (1 << 10),
|
||||
usbip_debug_stub_tx = (1 << 11),
|
||||
|
||||
usbip_debug_vhci_rh = (1 << 8),
|
||||
usbip_debug_vhci_hc = (1 << 9),
|
||||
usbip_debug_vhci_rx = (1 << 10),
|
||||
usbip_debug_vhci_tx = (1 << 11),
|
||||
usbip_debug_vhci_sysfs = (1 << 12)
|
||||
};
|
||||
|
||||
#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit)
|
||||
#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh)
|
||||
#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc)
|
||||
#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx)
|
||||
#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)
|
||||
|
||||
extern unsigned long usbip_debug_flag;
|
||||
extern struct device_attribute dev_attr_usbip_debug;
|
||||
|
||||
#define usbip_dbg_with_flag(flag, fmt, args...) \
|
||||
do { \
|
||||
if (flag & usbip_debug_flag) \
|
||||
pr_debug(fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define usbip_dbg_sysfs(fmt, args...) \
|
||||
usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args)
|
||||
#define usbip_dbg_xmit(fmt, args...) \
|
||||
usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args)
|
||||
#define usbip_dbg_urb(fmt, args...) \
|
||||
usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args)
|
||||
#define usbip_dbg_eh(fmt, args...) \
|
||||
usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args)
|
||||
|
||||
#define usbip_dbg_vhci_rh(fmt, args...) \
|
||||
usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args)
|
||||
#define usbip_dbg_vhci_hc(fmt, args...) \
|
||||
usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args)
|
||||
#define usbip_dbg_vhci_rx(fmt, args...) \
|
||||
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...) \
|
||||
usbip_dbg_with_flag(usbip_debug_vhci_sysfs, 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...) \
|
||||
usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args)
|
||||
#define usbip_dbg_stub_tx(fmt, args...) \
|
||||
usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args)
|
||||
|
||||
/*
|
||||
* USB/IP request headers
|
||||
*
|
||||
* 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)
|
||||
*
|
||||
* - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT
|
||||
* (server to client)
|
||||
*
|
||||
* - 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)
|
||||
*
|
||||
*/
|
||||
#define USBIP_CMD_SUBMIT 0x0001
|
||||
#define USBIP_CMD_UNLINK 0x0002
|
||||
#define USBIP_RET_SUBMIT 0x0003
|
||||
#define USBIP_RET_UNLINK 0x0004
|
||||
|
||||
#define USBIP_DIR_OUT 0x00
|
||||
#define USBIP_DIR_IN 0x01
|
||||
|
||||
/**
|
||||
* 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;
|
||||
__u32 direction;
|
||||
__u32 ep;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
__u32 transfer_flags;
|
||||
__s32 transfer_buffer_length;
|
||||
|
||||
/* it is difficult for usbip to sync frames (reserved only?) */
|
||||
__s32 start_frame;
|
||||
__s32 number_of_packets;
|
||||
__s32 interval;
|
||||
|
||||
unsigned char setup[8];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
__s32 start_frame;
|
||||
__s32 number_of_packets;
|
||||
__s32 error_count;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header
|
||||
* @seqnum: the URB seqnum to unlink
|
||||
*/
|
||||
struct usbip_header_cmd_unlink {
|
||||
__u32 seqnum;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header
|
||||
* @status: return status of the request
|
||||
*/
|
||||
struct usbip_header_ret_unlink {
|
||||
__s32 status;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
union {
|
||||
struct usbip_header_cmd_submit cmd_submit;
|
||||
struct usbip_header_ret_submit ret_submit;
|
||||
struct usbip_header_cmd_unlink cmd_unlink;
|
||||
struct usbip_header_ret_unlink ret_unlink;
|
||||
} u;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
/* event handler */
|
||||
#define USBIP_EH_SHUTDOWN (1 << 0)
|
||||
#define USBIP_EH_BYE (1 << 1)
|
||||
#define USBIP_EH_RESET (1 << 2)
|
||||
#define USBIP_EH_UNUSABLE (1 << 3)
|
||||
|
||||
#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE)
|
||||
#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
|
||||
#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
|
||||
#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
|
||||
#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE)
|
||||
|
||||
#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE)
|
||||
#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET)
|
||||
#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 task_struct *eh;
|
||||
wait_queue_head_t eh_waitq;
|
||||
|
||||
struct eh_ops {
|
||||
void (*shutdown)(struct usbip_device *);
|
||||
void (*reset)(struct usbip_device *);
|
||||
void (*unusable)(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)
|
||||
|
||||
/* usbip_common.c */
|
||||
void usbip_dump_urb(struct urb *purb);
|
||||
void usbip_dump_header(struct usbip_header *pdu);
|
||||
|
||||
int usbip_recv(struct socket *sock, void *buf, int size);
|
||||
|
||||
void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd,
|
||||
int pack);
|
||||
void usbip_header_correct_endian(struct usbip_header *pdu, int send);
|
||||
|
||||
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_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);
|
||||
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);
|
||||
|
||||
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 */
|
128
drivers/usb/usbip/usbip_event.c
Normal file
128
drivers/usb/usbip/usbip_event.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "usbip_common.h"
|
||||
|
||||
static int event_handler(struct usbip_device *ud)
|
||||
{
|
||||
usbip_dbg_eh("enter\n");
|
||||
|
||||
/*
|
||||
* Events are handled by only this thread.
|
||||
*/
|
||||
while (usbip_event_happened(ud)) {
|
||||
usbip_dbg_eh("pending event %lx\n", ud->event);
|
||||
|
||||
/*
|
||||
* NOTE: shutdown must come first.
|
||||
* Shutdown the device.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/* Stop the error handler. */
|
||||
if (ud->event & USBIP_EH_BYE)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_handler_loop(void *data)
|
||||
{
|
||||
struct usbip_device *ud = data;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbip_start_eh(struct usbip_device *ud)
|
||||
{
|
||||
init_waitqueue_head(&ud->eh_waitq);
|
||||
ud->event = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_start_eh);
|
||||
|
||||
void usbip_stop_eh(struct usbip_device *ud)
|
||||
{
|
||||
if (ud->eh == current)
|
||||
return; /* do not wait for myself */
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ud->lock, flags);
|
||||
ud->event |= event;
|
||||
wake_up(&ud->eh_waitq);
|
||||
spin_unlock_irqrestore(&ud->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_event_add);
|
||||
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbip_event_happened);
|
358
drivers/usb/usbip/usbip_protocol.txt
Normal file
358
drivers/usb/usbip/usbip_protocol.txt
Normal file
|
@ -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.
|
129
drivers/usb/usbip/vhci.h
Normal file
129
drivers/usb/usbip/vhci.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
/*
|
||||
* devid specifies a remote usb device uniquely instead
|
||||
* of combination of busnum and devnum.
|
||||
*/
|
||||
__u32 devid;
|
||||
|
||||
/* speed of a remote device */
|
||||
enum usb_device_speed speed;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* vhci_priv is linked to one of them. */
|
||||
struct list_head priv_tx;
|
||||
struct list_head priv_rx;
|
||||
|
||||
/* vhci_unlink is linked to one of them */
|
||||
struct list_head unlink_tx;
|
||||
struct list_head unlink_rx;
|
||||
|
||||
/* vhci_tx thread sleeps for this queue */
|
||||
wait_queue_head_t waitq_tx;
|
||||
};
|
||||
|
||||
/* urb->hcpriv, use container_of() */
|
||||
struct vhci_priv {
|
||||
unsigned long seqnum;
|
||||
struct list_head list;
|
||||
|
||||
struct vhci_device *vdev;
|
||||
struct urb *urb;
|
||||
};
|
||||
|
||||
struct vhci_unlink {
|
||||
/* seqnum of this request */
|
||||
unsigned long seqnum;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
/* seqnum of the unlink target */
|
||||
unsigned long unlink_seqnum;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
u32 port_status[VHCI_NPORTS];
|
||||
|
||||
unsigned resuming:1;
|
||||
unsigned long re_timeout;
|
||||
|
||||
atomic_t seqnum;
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* wIndex shows the port number and begins from 1.
|
||||
* But, the index of this array begins from 0.
|
||||
*/
|
||||
struct vhci_device vdev[VHCI_NPORTS];
|
||||
};
|
||||
|
||||
extern struct vhci_hcd *the_controller;
|
||||
extern const struct attribute_group dev_attr_group;
|
||||
|
||||
/* vhci_hcd.c */
|
||||
void rh_port_connect(int rhport, enum usb_device_speed speed);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
return &the_controller->vdev[port];
|
||||
}
|
||||
|
||||
static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct vhci_hcd *) (hcd->hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci)
|
||||
{
|
||||
return container_of((void *) vhci, struct usb_hcd, hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct device *vhci_dev(struct vhci_hcd *vhci)
|
||||
{
|
||||
return vhci_to_hcd(vhci)->self.controller;
|
||||
}
|
||||
|
||||
#endif /* __USBIP_VHCI_H */
|
1171
drivers/usb/usbip/vhci_hcd.c
Normal file
1171
drivers/usb/usbip/vhci_hcd.c
Normal file
File diff suppressed because it is too large
Load diff
268
drivers/usb/usbip/vhci_rx.c
Normal file
268
drivers/usb/usbip/vhci_rx.c
Normal file
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "usbip_common.h"
|
||||
#include "vhci.h"
|
||||
|
||||
/* 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;
|
||||
|
||||
list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
|
||||
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);
|
||||
}
|
||||
|
||||
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_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) {
|
||||
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);
|
||||
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);
|
||||
|
||||
usbip_dbg_vhci_rx("Leave\n");
|
||||
}
|
||||
|
||||
static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
|
||||
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) {
|
||||
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);
|
||||
list_del(&unlink->list);
|
||||
|
||||
spin_unlock(&vdev->priv_lock);
|
||||
return unlink;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&vdev->priv_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vhci_recv_ret_unlink(struct vhci_device *vdev,
|
||||
struct usbip_header *pdu)
|
||||
{
|
||||
struct vhci_unlink *unlink;
|
||||
struct urb *urb;
|
||||
|
||||
usbip_dump_header(pdu);
|
||||
|
||||
unlink = dequeue_pending_unlink(vdev, pdu);
|
||||
if (!unlink) {
|
||||
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.
|
||||
*/
|
||||
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 successful, status is -ECONNRESET */
|
||||
urb->status = pdu->u.ret_unlink.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);
|
||||
}
|
||||
|
||||
kfree(unlink);
|
||||
}
|
||||
|
||||
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 */
|
||||
static void vhci_rx_pdu(struct usbip_device *ud)
|
||||
{
|
||||
int ret;
|
||||
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);
|
||||
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
usbip_header_correct_endian(&pdu, 0);
|
||||
|
||||
if (usbip_dbg_flag_vhci_rx)
|
||||
usbip_dump_header(&pdu);
|
||||
|
||||
switch (pdu.base.command) {
|
||||
case USBIP_RET_SUBMIT:
|
||||
vhci_recv_ret_submit(vdev, &pdu);
|
||||
break;
|
||||
case USBIP_RET_UNLINK:
|
||||
vhci_recv_ret_unlink(vdev, &pdu);
|
||||
break;
|
||||
default:
|
||||
/* NOT REACHED */
|
||||
pr_err("unknown pdu %u\n", pdu.base.command);
|
||||
usbip_dump_header(&pdu);
|
||||
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int vhci_rx_loop(void *data)
|
||||
{
|
||||
struct usbip_device *ud = data;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (usbip_event_happened(ud))
|
||||
break;
|
||||
|
||||
vhci_rx_pdu(ud);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
252
drivers/usb/usbip/vhci_sysfs.c
Normal file
252
drivers/usb/usbip/vhci_sysfs.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/net.h>
|
||||
|
||||
#include "usbip_common.h"
|
||||
#include "vhci.h"
|
||||
|
||||
/* TODO: refine locking ?*/
|
||||
|
||||
/* Sysfs entry to show port status */
|
||||
static ssize_t status_show(struct device *dev, struct device_attribute *attr,
|
||||
char *out)
|
||||
{
|
||||
char *s = out;
|
||||
int i = 0;
|
||||
|
||||
BUG_ON(!the_controller || !out);
|
||||
|
||||
spin_lock(&the_controller->lock);
|
||||
|
||||
/*
|
||||
* output example:
|
||||
* prt sta spd dev socket local_busid
|
||||
* 000 004 000 000 c5a7bb80 1-2.3
|
||||
* 001 004 000 000 d8cee980 2-3.4
|
||||
*
|
||||
* IP address can be retrieved from a socket pointer address by looking
|
||||
* up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
|
||||
* port number and its peer IP address.
|
||||
*/
|
||||
out += sprintf(out,
|
||||
"prt sta spd bus dev socket local_busid\n");
|
||||
|
||||
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);
|
||||
out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
|
||||
out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
|
||||
|
||||
} else {
|
||||
out += sprintf(out, "000 000 000 0000000000000000 0-0");
|
||||
}
|
||||
|
||||
out += sprintf(out, "\n");
|
||||
spin_unlock(&vdev->ud.lock);
|
||||
}
|
||||
|
||||
spin_unlock(&the_controller->lock);
|
||||
|
||||
return out - s;
|
||||
}
|
||||
static DEVICE_ATTR_RO(status);
|
||||
|
||||
/* Sysfs entry to shutdown a virtual connection */
|
||||
static int vhci_port_disconnect(__u32 rhport)
|
||||
{
|
||||
struct vhci_device *vdev;
|
||||
|
||||
usbip_dbg_vhci_sysfs("enter\n");
|
||||
|
||||
/* lock */
|
||||
spin_lock(&the_controller->lock);
|
||||
|
||||
vdev = port_to_vdev(rhport);
|
||||
|
||||
spin_lock(&vdev->ud.lock);
|
||||
if (vdev->ud.status == VDEV_ST_NULL) {
|
||||
pr_err("not connected %d\n", vdev->ud.status);
|
||||
|
||||
/* unlock */
|
||||
spin_unlock(&vdev->ud.lock);
|
||||
spin_unlock(&the_controller->lock);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* unlock */
|
||||
spin_unlock(&vdev->ud.lock);
|
||||
spin_unlock(&the_controller->lock);
|
||||
|
||||
usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int err;
|
||||
__u32 rhport = 0;
|
||||
|
||||
if (sscanf(buf, "%u", &rhport) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* check rhport */
|
||||
if (rhport >= VHCI_NPORTS) {
|
||||
dev_err(dev, "invalid port %u\n", rhport);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = vhci_port_disconnect(rhport);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
|
||||
usbip_dbg_vhci_sysfs("Leave\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
|
||||
|
||||
/* Sysfs entry to establish a virtual connection */
|
||||
static int valid_args(__u32 rhport, enum usb_device_speed speed)
|
||||
{
|
||||
/* check rhport */
|
||||
if (rhport >= VHCI_NPORTS) {
|
||||
pr_err("port %u\n", rhport);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check speed */
|
||||
switch (speed) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
case USB_SPEED_WIRELESS:
|
||||
break;
|
||||
default:
|
||||
pr_err("Failed attach request for unsupported USB speed: %s\n",
|
||||
usb_speed_string(speed));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* To start a new USB/IP attachment, a userland program needs to setup a TCP
|
||||
* connection and then write its socket descriptor with remote device
|
||||
* information into this sysfs file.
|
||||
*
|
||||
* A remote device is virtually attached to the root-hub port of @rhport with
|
||||
* @speed. @devid is embedded into a request to specify the remote device in a
|
||||
* server host.
|
||||
*
|
||||
* write() returns 0 on success, else negative errno.
|
||||
*/
|
||||
static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct vhci_device *vdev;
|
||||
struct socket *socket;
|
||||
int sockfd = 0;
|
||||
__u32 rhport = 0, devid = 0, speed = 0;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* @rhport: port number of vhci_hcd
|
||||
* @sockfd: socket descriptor of an established TCP connection
|
||||
* @devid: unique device identifier in a remote host
|
||||
* @speed: usb device speed in a remote host
|
||||
*/
|
||||
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);
|
||||
|
||||
/* check received parameters */
|
||||
if (valid_args(rhport, speed) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Extract socket from fd. */
|
||||
socket = sockfd_lookup(sockfd, &err);
|
||||
if (!socket)
|
||||
return -EINVAL;
|
||||
|
||||
/* now need lock until setting vdev status as used */
|
||||
|
||||
/* begin a lock */
|
||||
spin_lock(&the_controller->lock);
|
||||
vdev = port_to_vdev(rhport);
|
||||
spin_lock(&vdev->ud.lock);
|
||||
|
||||
if (vdev->ud.status != VDEV_ST_NULL) {
|
||||
/* end of the lock */
|
||||
spin_unlock(&vdev->ud.lock);
|
||||
spin_unlock(&the_controller->lock);
|
||||
|
||||
sockfd_put(socket);
|
||||
|
||||
dev_err(dev, "port %d already used\n", rhport);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(dev,
|
||||
"rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n",
|
||||
rhport, sockfd, devid, speed, usb_speed_string(speed));
|
||||
|
||||
vdev->devid = devid;
|
||||
vdev->speed = speed;
|
||||
vdev->ud.tcp_socket = socket;
|
||||
vdev->ud.status = VDEV_ST_NOTASSIGNED;
|
||||
|
||||
spin_unlock(&vdev->ud.lock);
|
||||
spin_unlock(&the_controller->lock);
|
||||
/* end the lock */
|
||||
|
||||
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);
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);
|
||||
|
||||
static struct attribute *dev_attrs[] = {
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_detach.attr,
|
||||
&dev_attr_attach.attr,
|
||||
&dev_attr_usbip_debug.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
const struct attribute_group dev_attr_group = {
|
||||
.attrs = dev_attrs,
|
||||
};
|
224
drivers/usb/usbip/vhci_tx.c
Normal file
224
drivers/usb/usbip/vhci_tx.c
Normal file
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright (C) 2003-2008 Takahiro Hirofuchi
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
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);
|
||||
|
||||
if (urb->setup_packet)
|
||||
memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8);
|
||||
}
|
||||
|
||||
static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev)
|
||||
{
|
||||
struct vhci_priv *priv, *tmp;
|
||||
|
||||
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(&vdev->priv_lock);
|
||||
return priv;
|
||||
}
|
||||
|
||||
spin_unlock(&vdev->priv_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int vhci_send_cmd_submit(struct vhci_device *vdev)
|
||||
{
|
||||
struct vhci_priv *priv = NULL;
|
||||
|
||||
struct msghdr msg;
|
||||
struct kvec iov[3];
|
||||
size_t txsize;
|
||||
|
||||
size_t total_size = 0;
|
||||
|
||||
while ((priv = dequeue_from_priv_tx(vdev)) != NULL) {
|
||||
int ret;
|
||||
struct urb *urb = priv->urb;
|
||||
struct usbip_header pdu_header;
|
||||
struct usbip_iso_packet_descriptor *iso_buffer = NULL;
|
||||
|
||||
txsize = 0;
|
||||
memset(&pdu_header, 0, sizeof(pdu_header));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
|
||||
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);
|
||||
|
||||
iov[0].iov_base = &pdu_header;
|
||||
iov[0].iov_len = sizeof(pdu_header);
|
||||
txsize += sizeof(pdu_header);
|
||||
|
||||
/* 2. setup transfer buffer */
|
||||
if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) {
|
||||
iov[1].iov_base = urb->transfer_buffer;
|
||||
iov[1].iov_len = urb->transfer_buffer_length;
|
||||
txsize += urb->transfer_buffer_length;
|
||||
}
|
||||
|
||||
/* 3. setup iso_packet_descriptor */
|
||||
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
|
||||
ssize_t len = 0;
|
||||
|
||||
iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len);
|
||||
if (!iso_buffer) {
|
||||
usbip_event_add(&vdev->ud,
|
||||
SDEV_EVENT_ERROR_MALLOC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iov[2].iov_base = iso_buffer;
|
||||
iov[2].iov_len = len;
|
||||
txsize += len;
|
||||
}
|
||||
|
||||
ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize);
|
||||
if (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;
|
||||
}
|
||||
|
||||
kfree(iso_buffer);
|
||||
usbip_dbg_vhci_tx("send txdata\n");
|
||||
|
||||
total_size += txsize;
|
||||
}
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)
|
||||
{
|
||||
struct vhci_unlink *unlink, *tmp;
|
||||
|
||||
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(&vdev->priv_lock);
|
||||
return unlink;
|
||||
}
|
||||
|
||||
spin_unlock(&vdev->priv_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int vhci_send_cmd_unlink(struct vhci_device *vdev)
|
||||
{
|
||||
struct vhci_unlink *unlink = NULL;
|
||||
|
||||
struct msghdr msg;
|
||||
struct kvec iov[3];
|
||||
size_t txsize;
|
||||
|
||||
size_t total_size = 0;
|
||||
|
||||
while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) {
|
||||
int ret;
|
||||
struct usbip_header pdu_header;
|
||||
|
||||
txsize = 0;
|
||||
memset(&pdu_header, 0, sizeof(pdu_header));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
|
||||
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;
|
||||
pdu_header.base.devid = vdev->devid;
|
||||
pdu_header.base.ep = 0;
|
||||
pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum;
|
||||
|
||||
usbip_header_correct_endian(&pdu_header, 1);
|
||||
|
||||
iov[0].iov_base = &pdu_header;
|
||||
iov[0].iov_len = sizeof(pdu_header);
|
||||
txsize += sizeof(pdu_header);
|
||||
|
||||
ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize);
|
||||
if (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;
|
||||
}
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
||||
int vhci_tx_loop(void *data)
|
||||
{
|
||||
struct usbip_device *ud = data;
|
||||
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (vhci_send_cmd_submit(vdev) < 0)
|
||||
break;
|
||||
|
||||
if (vhci_send_cmd_unlink(vdev) < 0)
|
||||
break;
|
||||
|
||||
wait_event_interruptible(vdev->waitq_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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue