You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
usbip: move usbip kernel code out of staging
At this point, USB/IP kernel code is fully functional and can be moved out of staging. Signed-off-by: Valentina Manea <valentina.manea.m@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
588b48caf6
commit
96c2737716
@@ -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.
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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);
|
||||
@@ -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.
|
||||
@@ -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 */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user