Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
config USBIP_CORE
tristate "USB/IP support"
depends on USB && NET
default N
---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
default N
---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
default N
---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
default N
---help---
This enables the debug messages from the USB/IP drivers.

View 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

View 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>

View File

@@ -0,0 +1,112 @@
/*
* 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;
char shutdown_busid;
};
/* stub_priv is allocated from stub_priv_cache */
extern struct kmem_cache *stub_priv_cache;
/* stub_dev.c */
extern struct usb_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 */

View File

@@ -0,0 +1,546 @@
/*
* 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"
/*
* Define device IDs here if you want to explicitly limit exportable devices.
* In most cases, wildcard matching will be okay because driver binding can be
* changed dynamically by a userland program.
*/
static struct usb_device_id stub_table[] = {
#if 0
/* just an example */
{ USB_DEVICE(0x05ac, 0x0301) }, /* Mac 1 button mouse */
{ USB_DEVICE(0x0430, 0x0009) }, /* Plat Home Keyboard */
{ USB_DEVICE(0x059b, 0x0001) }, /* Iomega USB Zip 100 */
{ USB_DEVICE(0x04b3, 0x4427) }, /* IBM USB CD-ROM */
{ USB_DEVICE(0x05a9, 0xa511) }, /* LifeView USB cam */
{ USB_DEVICE(0x55aa, 0x0201) }, /* Imation card reader */
{ USB_DEVICE(0x046d, 0x0870) }, /* Qcam Express(QV-30) */
{ USB_DEVICE(0x04bb, 0x0101) }, /* IO-DATA HD 120GB */
{ USB_DEVICE(0x04bb, 0x0904) }, /* IO-DATA USB-ET/TX */
{ USB_DEVICE(0x04bb, 0x0201) }, /* IO-DATA USB-ET/TX */
{ USB_DEVICE(0x08bb, 0x2702) }, /* ONKYO USB Speaker */
{ USB_DEVICE(0x046d, 0x08b2) }, /* Logicool Qcam 4000 Pro */
#endif
/* magic for wild card */
{ .driver_info = 1 },
{ 0, } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, stub_table);
/*
* usbip_status shows the status of usbip-host as long as this driver is bound
* to the target device.
*/
static ssize_t show_status(struct device *dev, struct device_attribute *attr,
char *buf)
{
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(usbip_status, S_IRUGO, show_status, NULL);
/*
* 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;
ssize_t err = -EINVAL;
if (!sdev) {
dev_err(dev, "sdev is null\n");
return -ENODEV;
}
sscanf(buf, "%d", &sockfd);
if (sockfd != -1) {
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_to_socket(sockfd);
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 err;
}
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) {
fput(ud->tcp_socket->file);
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 usb_interface *interface)
{
struct stub_device *sdev;
int busnum = interface_to_busnum(interface);
int devnum = interface_to_devnum(interface);
dev_dbg(&interface->dev, "allocating stub device");
/* yes, it's a new device */
sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
if (!sdev)
return NULL;
sdev->interface = usb_get_intf(interface);
sdev->udev = usb_get_dev(udev);
/*
* devid is defined with devnum when this driver is first allocated.
* devnum may change later if a device is reset. However, devid never
* changes during a usbip connection.
*/
sdev->devid = (busnum << 16) | devnum;
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(&interface->dev, "register new interface\n");
return sdev;
}
static void stub_device_free(struct stub_device *sdev)
{
kfree(sdev);
}
/*
* If a usb device has multiple active interfaces, this driver is bound to all
* the active interfaces. However, usbip exports *a* usb device (i.e., not *an*
* active interface). Currently, a userland program must ensure that it
* looks at the usbip's sysfs entries of only the first active interface.
*
* TODO: use "struct usb_device_driver" to bind a usb device.
* However, it seems it is not fully supported in mainline kernel yet
* (2.6.19.2).
*/
static int stub_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct stub_device *sdev = NULL;
const char *udev_busid = dev_name(interface->dev.parent);
int err = 0;
struct bus_id_priv *busid_priv;
dev_dbg(&interface->dev, "Enter\n");
/* check we should claim or not by busid_table */
busid_priv = get_busid_priv(udev_busid);
if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) ||
(busid_priv->status == STUB_BUSID_OTHER)) {
dev_info(&interface->dev, "%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;
}
if (busid_priv->status == STUB_BUSID_ALLOC) {
sdev = busid_priv->sdev;
if (!sdev)
return -ENODEV;
busid_priv->interf_count++;
dev_info(&interface->dev, "usbip-host: register new interface "
"(bus %u dev %u ifn %u)\n",
udev->bus->busnum, udev->devnum,
interface->cur_altsetting->desc.bInterfaceNumber);
/* set private data to usb_interface */
usb_set_intfdata(interface, sdev);
err = stub_add_files(&interface->dev);
if (err) {
dev_err(&interface->dev, "stub_add_files for %s\n",
udev_busid);
usb_set_intfdata(interface, NULL);
busid_priv->interf_count--;
return err;
}
usb_get_intf(interface);
return 0;
}
/* ok, this is my device */
sdev = stub_device_alloc(udev, interface);
if (!sdev)
return -ENOMEM;
dev_info(&interface->dev, "usbip-host: register new device "
"(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum,
interface->cur_altsetting->desc.bInterfaceNumber);
busid_priv->interf_count = 0;
busid_priv->shutdown_busid = 0;
/* set private data to usb_interface */
usb_set_intfdata(interface, sdev);
busid_priv->interf_count++;
busid_priv->sdev = sdev;
err = stub_add_files(&interface->dev);
if (err) {
dev_err(&interface->dev, "stub_add_files for %s\n", udev_busid);
usb_set_intfdata(interface, NULL);
usb_put_intf(interface);
usb_put_dev(udev);
kthread_stop_put(sdev->ud.eh);
busid_priv->interf_count = 0;
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_interface *interface)
{
struct stub_device *sdev;
const char *udev_busid = dev_name(interface->dev.parent);
struct bus_id_priv *busid_priv;
dev_dbg(&interface->dev, "Enter\n");
busid_priv = get_busid_priv(udev_busid);
if (!busid_priv) {
BUG();
return;
}
sdev = usb_get_intfdata(interface);
/* get stub_device */
if (!sdev) {
dev_err(&interface->dev, "could not get device");
return;
}
usb_set_intfdata(interface, NULL);
/*
* NOTE: rx/tx threads are invoked for each usb_device.
*/
stub_remove_files(&interface->dev);
/* If usb reset is called from event handler */
if (busid_priv->sdev->ud.eh == current) {
busid_priv->interf_count--;
return;
}
if (busid_priv->interf_count > 1) {
busid_priv->interf_count--;
shutdown_busid(busid_priv);
usb_put_intf(interface);
return;
}
busid_priv->interf_count = 0;
/* shutdown the current connection */
shutdown_busid(busid_priv);
usb_put_dev(sdev->udev);
usb_put_intf(interface);
/* 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);
}
}
/*
* Presence of pre_reset and post_reset prevents the driver from being unbound
* when the device is being reset
*/
static int stub_pre_reset(struct usb_interface *interface)
{
dev_dbg(&interface->dev, "pre_reset\n");
return 0;
}
static int stub_post_reset(struct usb_interface *interface)
{
dev_dbg(&interface->dev, "post_reset\n");
return 0;
}
struct usb_driver stub_driver = {
.name = "usbip-host",
.probe = stub_probe,
.disconnect = stub_disconnect,
.id_table = stub_table,
.pre_reset = stub_pre_reset,
.post_reset = stub_post_reset,
};

View File

@@ -0,0 +1,300 @@
/*
* 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 "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]) {
strncpy(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;
/* strnlen() does not include \0 */
len = strnlen(buf + 4, BUSID_SIZE);
/* busid needs to include \0 termination */
if (!(len < BUSID_SIZE))
return -EINVAL;
strncpy(busid, buf + 4, BUSID_SIZE);
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 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(&stub_driver);
if (ret < 0) {
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 < 0) {
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(&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);
/*
* deregister() calls stub_disconnect() for all devices. Device
* specific data is cleared in stub_disconnect().
*/
usb_deregister(&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);

View File

@@ -0,0 +1,603 @@
/*
* 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 usb_ctrlrequest *req;
__u16 config;
req = (struct usb_ctrlrequest *) urb->setup_packet;
config = le16_to_cpu(req->wValue);
/*
* I have never seen a multi-config device. Very rare.
* For most devices, this will be called to choose a default
* configuration only once in an initialization phase.
*
* set_configuration may change a device configuration and its device
* drivers will be unbound and assigned for a new device configuration.
* This means this usbip driver will be also unbound when called, then
* eventually reassigned to the device as far as driver matching
* condition is kept.
*
* Unfortunately, an existing usbip connection will be dropped
* due to this driver unbinding. So, skip here.
* A user may need to set a special configuration value before
* exporting the device.
*/
dev_info(&urb->dev->dev, "usb_set_configuration %d to %s... skip!\n",
config, dev_name(&urb->dev->dev));
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");
return;
}
/* 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->interface->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;
}

View File

@@ -0,0 +1,397 @@
/*
* 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 = kzalloc(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;
}

View File

@@ -0,0 +1,819 @@
/*
* 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 show_flag(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%lx\n", usbip_debug_flag);
}
static ssize_t store_flag(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
sscanf(buf, "%lx", &usbip_debug_flag);
return count;
}
DEVICE_ATTR(usbip_debug, (S_IRUGO | S_IWUSR), show_flag, store_flag);
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) ",
udev->devnum, udev->devpath);
switch (udev->speed) {
case USB_SPEED_HIGH:
pr_debug("SPD_HIGH ");
break;
case USB_SPEED_FULL:
pr_debug("SPD_FULL ");
break;
case USB_SPEED_LOW:
pr_debug("SPD_LOW ");
break;
case USB_SPEED_UNKNOWN:
pr_debug("SPD_UNKNOWN ");
break;
default:
pr_debug("SPD_ERROR ");
break;
}
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_namelen = 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);
struct socket *sockfd_to_socket(unsigned int sockfd)
{
struct socket *socket;
struct file *file;
struct inode *inode;
file = fget(sockfd);
if (!file) {
pr_err("invalid sockfd\n");
return NULL;
}
inode = file_inode(file);
if (!inode || !S_ISSOCK(inode->i_mode)) {
fput(file);
return NULL;
}
socket = SOCKET_I(inode);
return socket;
}
EXPORT_SYMBOL_GPL(sockfd_to_socket);
/* 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);

View File

@@ -0,0 +1,349 @@
/*
* 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>
#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,
};
enum usbip_status {
/* sdev is available. */
SDEV_ST_AVAILABLE = 0x01,
/* sdev is now used. */
SDEV_ST_USED,
/* sdev is unusable because of a fatal error. */
SDEV_ST_ERROR,
/* vdev does not connect a remote device. */
VDEV_ST_NULL,
/* vdev is used, but the USB address is not assigned yet */
VDEV_ST_NOTASSIGNED,
VDEV_ST_USED,
VDEV_ST_ERROR
};
/* 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_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);
struct socket *sockfd_to_socket(unsigned int sockfd);
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 */

View 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_warning("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);

View 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.

View File

@@ -0,0 +1,28 @@
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
depcomp
install-sh
libsrc/Makefile
libsrc/Makefile.in
libtool
ltmain.sh
missing
src/Makefile
src/Makefile.in
stamp-h1
libsrc/libusbip.la
libsrc/libusbip_la-names.lo
libsrc/libusbip_la-usbip_common.lo
libsrc/libusbip_la-usbip_host_driver.lo
libsrc/libusbip_la-vhci_driver.lo
src/usbip
src/usbipd

View File

@@ -0,0 +1,3 @@
Takahiro Hirofuchi
Robert Leibl
matt mooney <mfm@muteddisk.com>

View File

@@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -0,0 +1,237 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
6. Often, you can also type `make uninstall' to remove the installed
files again.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@@ -0,0 +1,6 @@
SUBDIRS := libsrc src
includedir = @includedir@/usbip
include_HEADERS := $(addprefix libsrc/, \
usbip_common.h vhci_driver.h usbip_host_driver.h)
dist_man_MANS := $(addprefix doc/, usbip.8 usbipd.8)

View File

@@ -0,0 +1,199 @@
#
# README for usbip-utils
#
# Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
# 2005-2008 Takahiro Hirofuchi
[Requirements]
- USB/IP device drivers
Found in the staging directory of the Linux kernel.
- sysfsutils >= 2.0.0
sysfsutils library
- libwrap0-dev
tcp wrapper library
- gcc >= 4.0
- libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config
[Install]
0. Generate configuration scripts.
$ ./autogen.sh
1. Compile & install the userspace utilities.
$ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>]
$ make install
2. Compile & install USB/IP drivers.
[Usage]
server:# (Physically attach your USB device.)
server:# insmod usbip-core.ko
server:# insmod usbip-host.ko
server:# usbipd -D
- Start usbip daemon.
server:# usbip list -l
- List driver assignments for USB devices.
server:# usbip bind --busid 1-2
- Bind usbip-host.ko to the device with busid 1-2.
- The USB device 1-2 is now exportable to other hosts!
- Use `usbip unbind --busid 1-2' to stop exporting the device.
client:# insmod usbip-core.ko
client:# insmod vhci-hcd.ko
client:# usbip list --remote <host>
- List exported USB devices on the <host>.
client:# usbip attach --remote <host> --busid 1-2
- Connect the remote USB device.
client:# usbip port
- Show virtual port status.
client:# usbip detach --port <port>
- Detach the USB device.
[Example]
---------------------------
SERVER SIDE
---------------------------
Physically attach your USB devices to this host.
trois:# insmod path/to/usbip-core.ko
trois:# insmod path/to/usbip-host.ko
trois:# usbipd -D
In another terminal, let's look up what USB devices are physically
attached to this host.
trois:# usbip list -l
Local USB devices
=================
- busid 1-1 (05a9:a511)
1-1:1.0 -> ov511
- busid 3-2 (0711:0902)
3-2:1.0 -> none
- busid 3-3.1 (08bb:2702)
3-3.1:1.0 -> snd-usb-audio
3-3.1:1.1 -> snd-usb-audio
- busid 3-3.2 (04bb:0206)
3-3.2:1.0 -> usb-storage
- busid 3-3 (0409:0058)
3-3:1.0 -> hub
- busid 4-1 (046d:08b2)
4-1:1.0 -> none
4-1:1.1 -> none
4-1:1.2 -> none
- busid 5-2 (058f:9254)
5-2:1.0 -> hub
A USB storage device of busid 3-3.2 is now bound to the usb-storage
driver. To export this device, we first mark the device as
"exportable"; the device is bound to the usbip-host driver. Please
remember you can not export a USB hub.
Mark the device of busid 3-3.2 as exportable:
trois:# usbip --debug bind --busid 3-3.2
...
usbip debug: usbip_bind.c:162:[unbind_other] 3-3.2:1.0 -> usb-storage
...
bind device on busid 3-3.2: complete
trois:# usbip list -l
Local USB devices
=================
...
- busid 3-3.2 (04bb:0206)
3-3.2:1.0 -> usbip-host
...
---------------------------
CLIENT SIDE
---------------------------
First, let's list available remote devices that are marked as
exportable on the host.
deux:# insmod path/to/usbip-core.ko
deux:# insmod path/to/vhci-hcd.ko
deux:# usbip list --remote 10.0.0.3
Exportable USB devices
======================
- 10.0.0.3
1-1: Prolific Technology, Inc. : unknown product (067b:3507)
: /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-1
: (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
: 0 - Mass Storage / SCSI / Bulk (Zip) (08/06/50)
1-2.2.1: Apple Computer, Inc. : unknown product (05ac:0203)
: /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.1
: (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
: 0 - Human Interface Devices / Boot Interface Subclass / Keyboard (03/01/01)
1-2.2.3: OmniVision Technologies, Inc. : OV511+ WebCam (05a9:a511)
: /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.3
: (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
: 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/00)
3-1: Logitech, Inc. : QuickCam Pro 4000 (046d:08b2)
: /sys/devices/pci0000:00/0000:00:1e.0/0000:02:0a.0/usb3/3-1
: (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00)
: 0 - Data / unknown subclass / unknown protocol (0a/ff/00)
: 1 - Audio / Control Device / unknown protocol (01/01/00)
: 2 - Audio / Streaming / unknown protocol (01/02/00)
Attach a remote USB device:
deux:# usbip attach --remote 10.0.0.3 --busid 1-1
port 0 attached
Show the devices attached to this client:
deux:# usbip port
Port 00: <Port in Use> at Full Speed(12Mbps)
Prolific Technology, Inc. : unknown product (067b:3507)
6-1 -> usbip://10.0.0.3:3240/1-1 (remote bus/dev 001/004)
6-1:1.0 used by usb-storage
/sys/class/scsi_device/0:0:0:0/device
/sys/class/scsi_host/host0/device
/sys/block/sda/device
Detach the imported device:
deux:# usbip detach --port 0
port 0 detached
[Checklist]
- See 'Debug Tips' on the project wiki.
- http://usbip.wiki.sourceforge.net/how-to-debug-usbip
- usbip-host.ko must be bound to the target device.
- See /proc/bus/usb/devices and find "Driver=..." lines of the device.
- Shutdown firewall.
- usbip now uses TCP port 3240.
- Disable SELinux.
- If possible, compile your kernel with CONFIG_USB_DEBUG flag and try again.
- Check the kernel and daemon messages.
[Contact]
Mailing List: linux-usb@vger.kernel.org

View File

@@ -0,0 +1,9 @@
#!/bin/sh -x
#aclocal
#autoheader
#libtoolize --copy --force
#automake-1.9 -acf
#autoconf
autoreconf -i -f -v

View File

@@ -0,0 +1,12 @@
#!/bin/sh
if [ -r Makefile ]; then
make distclean
fi
FILES="aclocal.m4 autom4te.cache compile config.guess config.h.in config.log \
config.status config.sub configure cscope.out depcomp install-sh \
libsrc/Makefile libsrc/Makefile.in libtool ltmain.sh Makefile \
Makefile.in missing src/Makefile src/Makefile.in"
rm -vRf $FILES

View File

@@ -0,0 +1,112 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT([usbip-utils], [1.1.1], [linux-usb@vger.kernel.org])
AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number])
CURRENT=0
REVISION=1
AGE=0
AC_SUBST([LIBUSBIP_VERSION], [$CURRENT:$REVISION:$AGE], [library version])
AC_CONFIG_SRCDIR([src/usbipd.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign])
LT_INIT
# Silent build for automake >= 1.11
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_SUBST([EXTRA_CFLAGS], ["-Wall -Werror -Wextra -std=gnu99"])
# Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_MAKE_SET
# Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h dnl
string.h sys/socket.h syslog.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_INT32_T
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT8_T
# Checks for library functions.
AC_FUNC_REALLOC
AC_CHECK_FUNCS([memset mkdir regcomp socket strchr strerror strstr dnl
strtoul])
AC_CHECK_HEADER([sysfs/libsysfs.h],
[AC_CHECK_LIB([sysfs], [sysfs_open_directory_list],
[LIBS="$LIBS -lsysfs"],
[AC_MSG_ERROR([Missing sysfs2 library!])])],
[AC_MSG_ERROR([Missing /usr/include/sysfs/libsysfs.h])])
# Checks for libwrap library.
AC_MSG_CHECKING([whether to use the libwrap (TCP wrappers) library])
AC_ARG_WITH([tcp-wrappers],
[AS_HELP_STRING([--with-tcp-wrappers],
[use the libwrap (TCP wrappers) library])],
dnl [ACTION-IF-GIVEN]
[if test "$withval" = "yes"; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([for hosts_access in -lwrap])
saved_LIBS="$LIBS"
LIBS="-lwrap $saved_LIBS"
AC_TRY_LINK(
[int hosts_access(); int allow_severity, deny_severity;],
[hosts_access()],
[AC_MSG_RESULT([yes]);
AC_DEFINE([HAVE_LIBWRAP], [1],
[use tcp wrapper]) wrap_LIB="-lwrap"],
[AC_MSG_RESULT([not found]); exit 1])
else
AC_MSG_RESULT([no]);
LIBS="$saved_LIBS"
fi],
dnl [ACTION-IF-NOT-GIVEN]
[AC_MSG_RESULT([(default)])
AC_MSG_CHECKING([for hosts_access in -lwrap])
saved_LIBS="$LIBS"
LIBS="-lwrap $saved_LIBS"
AC_TRY_LINK(
[int hosts_access(); int allow_severity, deny_severity;],
[hosts_access()],
[AC_MSG_RESULT([yes]);
AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])],
[AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])])
# Sets directory containing usb.ids.
AC_ARG_WITH([usbids-dir],
[AS_HELP_STRING([--with-usbids-dir=DIR],
[where usb.ids is found (default /usr/share/hwdata/)])],
[USBIDS_DIR=$withval], [USBIDS_DIR="/usr/share/hwdata/"])
AC_SUBST([USBIDS_DIR])
# use _FORTIFY_SOURCE
AC_MSG_CHECKING([whether to use fortify])
AC_ARG_WITH([fortify],
[AS_HELP_STRING([--with-fortify],
[use _FORTIFY_SROUCE option when compiling)])],
dnl [ACTION-IF-GIVEN]
[if test "$withval" = "yes"; then
AC_MSG_RESULT([yes])
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE -O"
else
AC_MSG_RESULT([no])
CFLAGS="$CFLAGS -U_FORTIFY_SOURCE"
fi
],
dnl [ACTION-IF-NOT-GIVEN]
[AC_MSG_RESULT([default])])
AC_CONFIG_FILES([Makefile libsrc/Makefile src/Makefile])
AC_OUTPUT

View File

@@ -0,0 +1,89 @@
.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
.SH NAME
usbip \- manage USB/IP devices
.SH SYNOPSIS
.B usbip
[\foptions\R] <\fIcommand\fR> <\fIargs\fR>
.SH DESCRIPTION
On a USB/IP server, devices can be listed, bound, and unbound using
this program. On a USB/IP client, devices exported by USB/IP servers
can be listed, attached and detached.
.SH OPTIONS
.HP
\fB\-\-debug\fR
.IP
Print debugging information.
.PP
.HP
\fB\-\-log\fR
.IP
Log to syslog.
.PP
.SH COMMANDS
.HP
\fBversion\fR
.IP
Show version and exit.
.PP
.HP
\fBhelp\fR [\fIcommand\fR]
.IP
Print the program help message, or help on a specific command, and
then exit.
.PP
.HP
\fBattach\fR \-\-remote=<\fIhost\fR> \-\-busid=<\fIbus_id\fR>
.IP
Attach a remote USB device.
.PP
.HP
\fBdetach\fR \-\-port=<\fIport\fR>
.IP
Detach an imported USB device.
.PP
.HP
\fBbind\fR \-\-busid=<\fIbusid\fR>
.IP
Make a device exportable.
.PP
.HP
\fBunbind\fR \-\-busid=<\fIbusid\fR>
.IP
Stop exporting a device so it can be used by a local driver.
.PP
.HP
\fBlist\fR \-\-remote=<\fIhost\fR>
.IP
List USB devices exported by a remote host.
.PP
.HP
\fBlist\fR \-\-local
.IP
List local USB devices.
.PP
.SH EXAMPLES
client:# usbip list --remote=server
- List exportable usb devices on the server.
client:# usbip attach --remote=server --busid=1-2
- Connect the remote USB device.
client:# usbip detach --port=0
- Detach the usb device.
.SH "SEE ALSO"
\fBusbipd\fP\fB(8)\fB\fP

View File

@@ -0,0 +1,66 @@
.TH USBIP "8" "February 2009" "usbip" "System Administration Utilities"
.SH NAME
usbipd \- USB/IP server daemon
.SH SYNOPSIS
.B usbipd
[\fIoptions\fR]
.SH DESCRIPTION
.B usbipd
provides USB/IP clients access to exported USB devices.
Devices have to explicitly be exported using
.B usbip bind
before usbipd makes them available to other hosts.
The daemon accepts connections from USB/IP clients
on TCP port 3240.
.SH OPTIONS
.HP
\fB\-D\fR, \fB\-\-daemon\fR
.IP
Run as a daemon process.
.PP
.HP
\fB\-d\fR, \fB\-\-debug\fR
.IP
Print debugging information.
.PP
\fB\-h\fR, \fB\-\-help\fR
.IP
Print the program help message and exit.
.PP
.HP
\fB\-v\fR, \fB\-\-version\fR
.IP
Show version.
.PP
.SH LIMITATIONS
.B usbipd
offers no authentication or authorization for USB/IP. Any
USB/IP client can connect and use exported devices.
.SH EXAMPLES
server:# modprobe usbip
server:# usbipd -D
- Start usbip daemon.
server:# usbip list --local
- List driver assignments for usb devices.
server:# usbip bind --busid=1-2
- Bind usbip-host.ko to the device of busid 1-2.
- A usb device 1-2 is now exportable to other hosts!
- Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally.
.SH "SEE ALSO"
\fBusbip\fP\fB(8)\fB\fP

View File

@@ -0,0 +1,7 @@
libusbip_la_CPPFLAGS = -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
libusbip_la_CFLAGS = @EXTRA_CFLAGS@
libusbip_la_LDFLAGS = -version-info @LIBUSBIP_VERSION@
lib_LTLIBRARIES := libusbip.la
libusbip_la_SOURCES := names.c names.h usbip_host_driver.c usbip_host_driver.h \
usbip_common.c usbip_common.h vhci_driver.c vhci_driver.h

View File

@@ -0,0 +1,504 @@
/*
* names.c -- USB name database manipulation routines
*
* Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
*
*
*
* Copyright (C) 2005 Takahiro Hirofuchi
* - names_deinit() is added.
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include "names.h"
#include "usbip_common.h"
struct vendor {
struct vendor *next;
u_int16_t vendorid;
char name[1];
};
struct product {
struct product *next;
u_int16_t vendorid, productid;
char name[1];
};
struct class {
struct class *next;
u_int8_t classid;
char name[1];
};
struct subclass {
struct subclass *next;
u_int8_t classid, subclassid;
char name[1];
};
struct protocol {
struct protocol *next;
u_int8_t classid, subclassid, protocolid;
char name[1];
};
struct genericstrtable {
struct genericstrtable *next;
unsigned int num;
char name[1];
};
#define HASH1 0x10
#define HASH2 0x02
#define HASHSZ 16
static unsigned int hashnum(unsigned int num)
{
unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27;
for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1)
if (num & mask1)
num ^= mask2;
return num & (HASHSZ-1);
}
static struct vendor *vendors[HASHSZ] = { NULL, };
static struct product *products[HASHSZ] = { NULL, };
static struct class *classes[HASHSZ] = { NULL, };
static struct subclass *subclasses[HASHSZ] = { NULL, };
static struct protocol *protocols[HASHSZ] = { NULL, };
const char *names_vendor(u_int16_t vendorid)
{
struct vendor *v;
v = vendors[hashnum(vendorid)];
for (; v; v = v->next)
if (v->vendorid == vendorid)
return v->name;
return NULL;
}
const char *names_product(u_int16_t vendorid, u_int16_t productid)
{
struct product *p;
p = products[hashnum((vendorid << 16) | productid)];
for (; p; p = p->next)
if (p->vendorid == vendorid && p->productid == productid)
return p->name;
return NULL;
}
const char *names_class(u_int8_t classid)
{
struct class *c;
c = classes[hashnum(classid)];
for (; c; c = c->next)
if (c->classid == classid)
return c->name;
return NULL;
}
const char *names_subclass(u_int8_t classid, u_int8_t subclassid)
{
struct subclass *s;
s = subclasses[hashnum((classid << 8) | subclassid)];
for (; s; s = s->next)
if (s->classid == classid && s->subclassid == subclassid)
return s->name;
return NULL;
}
const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
u_int8_t protocolid)
{
struct protocol *p;
p = protocols[hashnum((classid << 16) | (subclassid << 8)
| protocolid)];
for (; p; p = p->next)
if (p->classid == classid && p->subclassid == subclassid &&
p->protocolid == protocolid)
return p->name;
return NULL;
}
/* add a cleanup function by takahiro */
struct pool {
struct pool *next;
void *mem;
};
static struct pool *pool_head;
static void *my_malloc(size_t size)
{
struct pool *p;
p = calloc(1, sizeof(struct pool));
if (!p) {
free(p);
return NULL;
}
p->mem = calloc(1, size);
if (!p->mem)
return NULL;
p->next = pool_head;
pool_head = p;
return p->mem;
}
void names_free(void)
{
struct pool *pool;
if (!pool_head)
return;
for (pool = pool_head; pool != NULL; ) {
struct pool *tmp;
if (pool->mem)
free(pool->mem);
tmp = pool;
pool = pool->next;
free(tmp);
}
}
static int new_vendor(const char *name, u_int16_t vendorid)
{
struct vendor *v;
unsigned int h = hashnum(vendorid);
v = vendors[h];
for (; v; v = v->next)
if (v->vendorid == vendorid)
return -1;
v = my_malloc(sizeof(struct vendor) + strlen(name));
if (!v)
return -1;
strcpy(v->name, name);
v->vendorid = vendorid;
v->next = vendors[h];
vendors[h] = v;
return 0;
}
static int new_product(const char *name, u_int16_t vendorid,
u_int16_t productid)
{
struct product *p;
unsigned int h = hashnum((vendorid << 16) | productid);
p = products[h];
for (; p; p = p->next)
if (p->vendorid == vendorid && p->productid == productid)
return -1;
p = my_malloc(sizeof(struct product) + strlen(name));
if (!p)
return -1;
strcpy(p->name, name);
p->vendorid = vendorid;
p->productid = productid;
p->next = products[h];
products[h] = p;
return 0;
}
static int new_class(const char *name, u_int8_t classid)
{
struct class *c;
unsigned int h = hashnum(classid);
c = classes[h];
for (; c; c = c->next)
if (c->classid == classid)
return -1;
c = my_malloc(sizeof(struct class) + strlen(name));
if (!c)
return -1;
strcpy(c->name, name);
c->classid = classid;
c->next = classes[h];
classes[h] = c;
return 0;
}
static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid)
{
struct subclass *s;
unsigned int h = hashnum((classid << 8) | subclassid);
s = subclasses[h];
for (; s; s = s->next)
if (s->classid == classid && s->subclassid == subclassid)
return -1;
s = my_malloc(sizeof(struct subclass) + strlen(name));
if (!s)
return -1;
strcpy(s->name, name);
s->classid = classid;
s->subclassid = subclassid;
s->next = subclasses[h];
subclasses[h] = s;
return 0;
}
static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid,
u_int8_t protocolid)
{
struct protocol *p;
unsigned int h = hashnum((classid << 16) | (subclassid << 8)
| protocolid);
p = protocols[h];
for (; p; p = p->next)
if (p->classid == classid && p->subclassid == subclassid
&& p->protocolid == protocolid)
return -1;
p = my_malloc(sizeof(struct protocol) + strlen(name));
if (!p)
return -1;
strcpy(p->name, name);
p->classid = classid;
p->subclassid = subclassid;
p->protocolid = protocolid;
p->next = protocols[h];
protocols[h] = p;
return 0;
}
static void parse(FILE *f)
{
char buf[512], *cp;
unsigned int linectr = 0;
int lastvendor = -1;
int lastclass = -1;
int lastsubclass = -1;
int lasthut = -1;
int lastlang = -1;
unsigned int u;
while (fgets(buf, sizeof(buf), f)) {
linectr++;
/* remove line ends */
cp = strchr(buf, '\r');
if (cp)
*cp = 0;
cp = strchr(buf, '\n');
if (cp)
*cp = 0;
if (buf[0] == '#' || !buf[0])
continue;
cp = buf;
if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' &&
buf[3] == 'S' && buf[4] == 'D' &&
buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/
buf[7] == ' ') {
continue;
}
if (buf[0] == 'P' && buf[1] == 'H' &&
buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') {
continue;
}
if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' &&
buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') {
continue;
}
if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') {
lasthut = lastclass = lastvendor = lastsubclass = -1;
/*
* set 1 as pseudo-id to indicate that the parser is
* in a `L' section.
*/
lastlang = 1;
continue;
}
if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') {
/* class spec */
cp = buf+2;
while (isspace(*cp))
cp++;
if (!isxdigit(*cp)) {
err("Invalid class spec at line %u", linectr);
continue;
}
u = strtoul(cp, &cp, 16);
while (isspace(*cp))
cp++;
if (!*cp) {
err("Invalid class spec at line %u", linectr);
continue;
}
if (new_class(cp, u))
err("Duplicate class spec at line %u class %04x %s",
linectr, u, cp);
dbg("line %5u class %02x %s", linectr, u, cp);
lasthut = lastlang = lastvendor = lastsubclass = -1;
lastclass = u;
continue;
}
if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) {
/* audio terminal type spec */
continue;
}
if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C'
&& isspace(buf[3])) {
/* HID Descriptor bCountryCode */
continue;
}
if (isxdigit(*cp)) {
/* vendor */
u = strtoul(cp, &cp, 16);
while (isspace(*cp))
cp++;
if (!*cp) {
err("Invalid vendor spec at line %u", linectr);
continue;
}
if (new_vendor(cp, u))
err("Duplicate vendor spec at line %u vendor %04x %s",
linectr, u, cp);
dbg("line %5u vendor %04x %s", linectr, u, cp);
lastvendor = u;
lasthut = lastlang = lastclass = lastsubclass = -1;
continue;
}
if (buf[0] == '\t' && isxdigit(buf[1])) {
/* product or subclass spec */
u = strtoul(buf+1, &cp, 16);
while (isspace(*cp))
cp++;
if (!*cp) {
err("Invalid product/subclass spec at line %u",
linectr);
continue;
}
if (lastvendor != -1) {
if (new_product(cp, lastvendor, u))
err("Duplicate product spec at line %u product %04x:%04x %s",
linectr, lastvendor, u, cp);
dbg("line %5u product %04x:%04x %s", linectr,
lastvendor, u, cp);
continue;
}
if (lastclass != -1) {
if (new_subclass(cp, lastclass, u))
err("Duplicate subclass spec at line %u class %02x:%02x %s",
linectr, lastclass, u, cp);
dbg("line %5u subclass %02x:%02x %s", linectr,
lastclass, u, cp);
lastsubclass = u;
continue;
}
if (lasthut != -1) {
/* do not store hut */
continue;
}
if (lastlang != -1) {
/* do not store langid */
continue;
}
err("Product/Subclass spec without prior Vendor/Class spec at line %u",
linectr);
continue;
}
if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) {
/* protocol spec */
u = strtoul(buf+2, &cp, 16);
while (isspace(*cp))
cp++;
if (!*cp) {
err("Invalid protocol spec at line %u",
linectr);
continue;
}
if (lastclass != -1 && lastsubclass != -1) {
if (new_protocol(cp, lastclass, lastsubclass,
u))
err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s",
linectr, lastclass, lastsubclass,
u, cp);
dbg("line %5u protocol %02x:%02x:%02x %s",
linectr, lastclass, lastsubclass, u, cp);
continue;
}
err("Protocol spec without prior Class and Subclass spec at line %u",
linectr);
continue;
}
if (buf[0] == 'H' && buf[1] == 'I' &&
buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') {
continue;
}
if (buf[0] == 'H' && buf[1] == 'U' &&
buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') {
lastlang = lastclass = lastvendor = lastsubclass = -1;
/*
* set 1 as pseudo-id to indicate that the parser is
* in a `HUT' section.
*/
lasthut = 1;
continue;
}
if (buf[0] == 'R' && buf[1] == ' ')
continue;
if (buf[0] == 'V' && buf[1] == 'T')
continue;
err("Unknown line at line %u", linectr);
}
}
int names_init(char *n)
{
FILE *f;
f = fopen(n, "r");
if (!f)
return errno;
parse(f);
fclose(f);
return 0;
}

View File

@@ -0,0 +1,41 @@
/*
* names.h -- USB name database manipulation routines
*
* Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
*
* Copyright (C) 2005 Takahiro Hirofuchi
* - names_free() is added.
*/
#ifndef _NAMES_H
#define _NAMES_H
#include <sys/types.h>
/* used by usbip_common.c */
extern const char *names_vendor(u_int16_t vendorid);
extern const char *names_product(u_int16_t vendorid, u_int16_t productid);
extern const char *names_class(u_int8_t classid);
extern const char *names_subclass(u_int8_t classid, u_int8_t subclassid);
extern const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
u_int8_t protocolid);
extern int names_init(char *n);
extern void names_free(void);
#endif /* _NAMES_H */

View File

@@ -0,0 +1,292 @@
/*
* Copyright (C) 2005-2007 Takahiro Hirofuchi
*/
#include "usbip_common.h"
#include "names.h"
#undef PROGNAME
#define PROGNAME "libusbip"
int usbip_use_syslog;
int usbip_use_stderr;
int usbip_use_debug;
struct speed_string {
int num;
char *speed;
char *desc;
};
static const struct speed_string speed_strings[] = {
{ USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"},
{ USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" },
{ USB_SPEED_FULL, "12", "Full Speed(12Mbps)" },
{ USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
{ 0, NULL, NULL }
};
struct portst_string {
int num;
char *desc;
};
static struct portst_string portst_strings[] = {
{ SDEV_ST_AVAILABLE, "Device Available" },
{ SDEV_ST_USED, "Device in Use" },
{ SDEV_ST_ERROR, "Device Error"},
{ VDEV_ST_NULL, "Port Available"},
{ VDEV_ST_NOTASSIGNED, "Port Initializing"},
{ VDEV_ST_USED, "Port in Use"},
{ VDEV_ST_ERROR, "Port Error"},
{ 0, NULL}
};
const char *usbip_status_string(int32_t status)
{
for (int i = 0; portst_strings[i].desc != NULL; i++)
if (portst_strings[i].num == status)
return portst_strings[i].desc;
return "Unknown Status";
}
const char *usbip_speed_string(int num)
{
for (int i = 0; speed_strings[i].speed != NULL; i++)
if (speed_strings[i].num == num)
return speed_strings[i].desc;
return "Unknown Speed";
}
#define DBG_UDEV_INTEGER(name)\
dbg("%-20s = %x", to_string(name), (int) udev->name)
#define DBG_UINF_INTEGER(name)\
dbg("%-20s = %x", to_string(name), (int) uinf->name)
void dump_usb_interface(struct usbip_usb_interface *uinf)
{
char buff[100];
usbip_names_get_class(buff, sizeof(buff),
uinf->bInterfaceClass,
uinf->bInterfaceSubClass,
uinf->bInterfaceProtocol);
dbg("%-20s = %s", "Interface(C/SC/P)", buff);
}
void dump_usb_device(struct usbip_usb_device *udev)
{
char buff[100];
dbg("%-20s = %s", "path", udev->path);
dbg("%-20s = %s", "busid", udev->busid);
usbip_names_get_class(buff, sizeof(buff),
udev->bDeviceClass,
udev->bDeviceSubClass,
udev->bDeviceProtocol);
dbg("%-20s = %s", "Device(C/SC/P)", buff);
DBG_UDEV_INTEGER(bcdDevice);
usbip_names_get_product(buff, sizeof(buff),
udev->idVendor,
udev->idProduct);
dbg("%-20s = %s", "Vendor/Product", buff);
DBG_UDEV_INTEGER(bNumConfigurations);
DBG_UDEV_INTEGER(bNumInterfaces);
dbg("%-20s = %s", "speed",
usbip_speed_string(udev->speed));
DBG_UDEV_INTEGER(busnum);
DBG_UDEV_INTEGER(devnum);
}
int read_attr_value(struct sysfs_device *dev, const char *name,
const char *format)
{
char attrpath[SYSFS_PATH_MAX];
struct sysfs_attribute *attr;
int num = 0;
int ret;
snprintf(attrpath, sizeof(attrpath), "%s/%s", dev->path, name);
attr = sysfs_open_attribute(attrpath);
if (!attr) {
dbg("sysfs_open_attribute failed: %s", attrpath);
return 0;
}
ret = sysfs_read_attribute(attr);
if (ret < 0) {
dbg("sysfs_read_attribute failed");
goto err;
}
ret = sscanf(attr->value, format, &num);
if (ret < 1) {
dbg("sscanf failed");
goto err;
}
err:
sysfs_close_attribute(attr);
return num;
}
int read_attr_speed(struct sysfs_device *dev)
{
char attrpath[SYSFS_PATH_MAX];
struct sysfs_attribute *attr;
char speed[100];
int ret;
snprintf(attrpath, sizeof(attrpath), "%s/%s", dev->path, "speed");
attr = sysfs_open_attribute(attrpath);
if (!attr) {
dbg("sysfs_open_attribute failed: %s", attrpath);
return 0;
}
ret = sysfs_read_attribute(attr);
if (ret < 0) {
dbg("sysfs_read_attribute failed");
goto err;
}
ret = sscanf(attr->value, "%s\n", speed);
if (ret < 1) {
dbg("sscanf failed");
goto err;
}
err:
sysfs_close_attribute(attr);
for (int i = 0; speed_strings[i].speed != NULL; i++) {
if (!strcmp(speed, speed_strings[i].speed))
return speed_strings[i].num;
}
return USB_SPEED_UNKNOWN;
}
#define READ_ATTR(object, type, dev, name, format) \
do { \
(object)->name = (type) read_attr_value(dev, to_string(name), \
format); \
} while (0)
int read_usb_device(struct sysfs_device *sdev, struct usbip_usb_device *udev)
{
uint32_t busnum, devnum;
READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n");
READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n");
READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n");
READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n");
READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n");
READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n");
READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n");
READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n");
READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n");
READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n");
udev->speed = read_attr_speed(sdev);
strncpy(udev->path, sdev->path, SYSFS_PATH_MAX);
strncpy(udev->busid, sdev->name, SYSFS_BUS_ID_SIZE);
sscanf(sdev->name, "%u-%u", &busnum, &devnum);
udev->busnum = busnum;
return 0;
}
int read_usb_interface(struct usbip_usb_device *udev, int i,
struct usbip_usb_interface *uinf)
{
char busid[SYSFS_BUS_ID_SIZE];
struct sysfs_device *sif;
sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i);
sif = sysfs_open_device("usb", busid);
if (!sif) {
dbg("sysfs_open_device(\"usb\", \"%s\") failed", busid);
return -1;
}
READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n");
READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n");
READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n");
sysfs_close_device(sif);
return 0;
}
int usbip_names_init(char *f)
{
return names_init(f);
}
void usbip_names_free()
{
names_free();
}
void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
uint16_t product)
{
const char *prod, *vend;
prod = names_product(vendor, product);
if (!prod)
prod = "unknown product";
vend = names_vendor(vendor);
if (!vend)
vend = "unknown vendor";
snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product);
}
void usbip_names_get_class(char *buff, size_t size, uint8_t class,
uint8_t subclass, uint8_t protocol)
{
const char *c, *s, *p;
if (class == 0 && subclass == 0 && protocol == 0) {
snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol);
return;
}
p = names_protocol(class, subclass, protocol);
if (!p)
p = "unknown protocol";
s = names_subclass(class, subclass);
if (!s)
s = "unknown subclass";
c = names_class(class);
if (!c)
c = "unknown class";
snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2005-2007 Takahiro Hirofuchi
*/
#ifndef __USBIP_COMMON_H
#define __USBIP_COMMON_H
#include <sysfs/libsysfs.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#ifndef USBIDS_FILE
#define USBIDS_FILE "/usr/share/hwdata/usb.ids"
#endif
#ifndef VHCI_STATE_PATH
#define VHCI_STATE_PATH "/var/run/vhci_hcd"
#endif
/* kernel module names */
#define USBIP_CORE_MOD_NAME "usbip-core"
#define USBIP_HOST_DRV_NAME "usbip-host"
#define USBIP_VHCI_DRV_NAME "vhci_hcd"
extern int usbip_use_syslog;
extern int usbip_use_stderr;
extern int usbip_use_debug ;
#define PROGNAME "usbip"
#define pr_fmt(fmt) "%s: %s: " fmt "\n", PROGNAME
#define dbg_fmt(fmt) pr_fmt("%s:%d:[%s] " fmt), "debug", \
__FILE__, __LINE__, __FUNCTION__
#define err(fmt, args...) \
do { \
if (usbip_use_syslog) { \
syslog(LOG_ERR, pr_fmt(fmt), "error", ##args); \
} \
if (usbip_use_stderr) { \
fprintf(stderr, pr_fmt(fmt), "error", ##args); \
} \
} while (0)
#define info(fmt, args...) \
do { \
if (usbip_use_syslog) { \
syslog(LOG_INFO, pr_fmt(fmt), "info", ##args); \
} \
if (usbip_use_stderr) { \
fprintf(stderr, pr_fmt(fmt), "info", ##args); \
} \
} while (0)
#define dbg(fmt, args...) \
do { \
if (usbip_use_debug) { \
if (usbip_use_syslog) { \
syslog(LOG_DEBUG, dbg_fmt(fmt), ##args); \
} \
if (usbip_use_stderr) { \
fprintf(stderr, dbg_fmt(fmt), ##args); \
} \
} \
} while (0)
#define BUG() \
do { \
err("sorry, it's a bug!"); \
abort(); \
} while (0)
enum usb_device_speed {
USB_SPEED_UNKNOWN = 0, /* enumerating */
USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
USB_SPEED_HIGH, /* usb 2.0 */
USB_SPEED_VARIABLE /* wireless (usb 2.5) */
};
/* FIXME: how to sync with drivers/usbip_common.h ? */
enum usbip_device_status {
/* sdev is available. */
SDEV_ST_AVAILABLE = 0x01,
/* sdev is now used. */
SDEV_ST_USED,
/* sdev is unusable because of a fatal error. */
SDEV_ST_ERROR,
/* vdev does not connect a remote device. */
VDEV_ST_NULL,
/* vdev is used, but the USB address is not assigned yet */
VDEV_ST_NOTASSIGNED,
VDEV_ST_USED,
VDEV_ST_ERROR
};
struct usbip_usb_interface {
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t padding; /* alignment */
} __attribute__((packed));
struct usbip_usb_device {
char path[SYSFS_PATH_MAX];
char busid[SYSFS_BUS_ID_SIZE];
uint32_t busnum;
uint32_t devnum;
uint32_t speed;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bConfigurationValue;
uint8_t bNumConfigurations;
uint8_t bNumInterfaces;
} __attribute__((packed));
#define to_string(s) #s
void dump_usb_interface(struct usbip_usb_interface *);
void dump_usb_device(struct usbip_usb_device *);
int read_usb_device(struct sysfs_device *sdev, struct usbip_usb_device *udev);
int read_attr_value(struct sysfs_device *dev, const char *name,
const char *format);
int read_usb_interface(struct usbip_usb_device *udev, int i,
struct usbip_usb_interface *uinf);
const char *usbip_speed_string(int num);
const char *usbip_status_string(int32_t status);
int usbip_names_init(char *);
void usbip_names_free(void);
void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
uint16_t product);
void usbip_names_get_class(char *buff, size_t size, uint8_t class,
uint8_t subclass, uint8_t protocol);
#endif /* __USBIP_COMMON_H */

View File

@@ -0,0 +1,401 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include "usbip_common.h"
#include "usbip_host_driver.h"
#undef PROGNAME
#define PROGNAME "libusbip"
struct usbip_host_driver *host_driver;
#define SYSFS_OPEN_RETRIES 100
/* only the first interface value is true! */
static int32_t read_attr_usbip_status(struct usbip_usb_device *udev)
{
char attrpath[SYSFS_PATH_MAX];
struct sysfs_attribute *attr;
int value = 0;
int rc;
struct stat s;
int retries = SYSFS_OPEN_RETRIES;
/* This access is racy!
*
* Just after detach, our driver removes the sysfs
* files and recreates them.
*
* We may try and fail to open the usbip_status of
* an exported device in the (short) window where
* it has been removed and not yet recreated.
*
* This is a bug in the interface. Nothing we can do
* except work around it here by polling for the sysfs
* usbip_status to reappear.
*/
snprintf(attrpath, SYSFS_PATH_MAX, "%s/%s:%d.%d/usbip_status",
udev->path, udev->busid, udev->bConfigurationValue, 0);
while (retries > 0) {
if (stat(attrpath, &s) == 0)
break;
if (errno != ENOENT) {
dbg("stat failed: %s", attrpath);
return -1;
}
usleep(10000); /* 10ms */
retries--;
}
if (retries == 0)
dbg("usbip_status not ready after %d retries",
SYSFS_OPEN_RETRIES);
else if (retries < SYSFS_OPEN_RETRIES)
dbg("warning: usbip_status ready after %d retries",
SYSFS_OPEN_RETRIES - retries);
attr = sysfs_open_attribute(attrpath);
if (!attr) {
dbg("sysfs_open_attribute failed: %s", attrpath);
return -1;
}
rc = sysfs_read_attribute(attr);
if (rc) {
dbg("sysfs_read_attribute failed: %s", attrpath);
sysfs_close_attribute(attr);
return -1;
}
value = atoi(attr->value);
sysfs_close_attribute(attr);
return value;
}
static struct usbip_exported_device *usbip_exported_device_new(char *sdevpath)
{
struct usbip_exported_device *edev = NULL;
size_t size;
int i;
edev = calloc(1, sizeof(*edev));
if (!edev) {
dbg("calloc failed");
return NULL;
}
edev->sudev = sysfs_open_device_path(sdevpath);
if (!edev->sudev) {
dbg("sysfs_open_device_path failed: %s", sdevpath);
goto err;
}
read_usb_device(edev->sudev, &edev->udev);
edev->status = read_attr_usbip_status(&edev->udev);
if (edev->status < 0)
goto err;
/* reallocate buffer to include usb interface data */
size = sizeof(*edev) + edev->udev.bNumInterfaces *
sizeof(struct usbip_usb_interface);
edev = realloc(edev, size);
if (!edev) {
dbg("realloc failed");
goto err;
}
for (i = 0; i < edev->udev.bNumInterfaces; i++)
read_usb_interface(&edev->udev, i, &edev->uinf[i]);
return edev;
err:
if (edev && edev->sudev)
sysfs_close_device(edev->sudev);
if (edev)
free(edev);
return NULL;
}
static int check_new(struct dlist *dlist, struct sysfs_device *target)
{
struct sysfs_device *dev;
dlist_for_each_data(dlist, dev, struct sysfs_device) {
if (!strncmp(dev->bus_id, target->bus_id, SYSFS_BUS_ID_SIZE))
/* device found and is not new */
return 0;
}
return 1;
}
static void delete_nothing(void *unused_data)
{
/*
* NOTE: Do not delete anything, but the container will be deleted.
*/
(void) unused_data;
}
static int refresh_exported_devices(void)
{
/* sysfs_device of usb_interface */
struct sysfs_device *suintf;
struct dlist *suintf_list;
/* sysfs_device of usb_device */
struct sysfs_device *sudev;
struct dlist *sudev_list;
struct usbip_exported_device *edev;
sudev_list = dlist_new_with_delete(sizeof(struct sysfs_device),
delete_nothing);
suintf_list = sysfs_get_driver_devices(host_driver->sysfs_driver);
if (!suintf_list) {
/*
* Not an error condition. There are simply no devices bound to
* the driver yet.
*/
dbg("bind " USBIP_HOST_DRV_NAME ".ko to a usb device to be "
"exportable!");
return 0;
}
/* collect unique USB devices (not interfaces) */
dlist_for_each_data(suintf_list, suintf, struct sysfs_device) {
/* get usb device of this usb interface */
sudev = sysfs_get_device_parent(suintf);
if (!sudev) {
dbg("sysfs_get_device_parent failed: %s", suintf->name);
continue;
}
if (check_new(sudev_list, sudev)) {
/* insert item at head of list */
dlist_unshift(sudev_list, sudev);
}
}
dlist_for_each_data(sudev_list, sudev, struct sysfs_device) {
edev = usbip_exported_device_new(sudev->path);
if (!edev) {
dbg("usbip_exported_device_new failed");
continue;
}
dlist_unshift(host_driver->edev_list, edev);
host_driver->ndevs++;
}
dlist_destroy(sudev_list);
return 0;
}
static struct sysfs_driver *open_sysfs_host_driver(void)
{
char bus_type[] = "usb";
char sysfs_mntpath[SYSFS_PATH_MAX];
char host_drv_path[SYSFS_PATH_MAX];
struct sysfs_driver *host_drv;
int rc;
rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
if (rc < 0) {
dbg("sysfs_get_mnt_path failed");
return NULL;
}
snprintf(host_drv_path, SYSFS_PATH_MAX, "%s/%s/%s/%s/%s",
sysfs_mntpath, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
USBIP_HOST_DRV_NAME);
host_drv = sysfs_open_driver_path(host_drv_path);
if (!host_drv) {
dbg("sysfs_open_driver_path failed");
return NULL;
}
return host_drv;
}
static void usbip_exported_device_delete(void *dev)
{
struct usbip_exported_device *edev = dev;
sysfs_close_device(edev->sudev);
free(dev);
}
int usbip_host_driver_open(void)
{
int rc;
host_driver = calloc(1, sizeof(*host_driver));
if (!host_driver) {
dbg("calloc failed");
return -1;
}
host_driver->ndevs = 0;
host_driver->edev_list =
dlist_new_with_delete(sizeof(struct usbip_exported_device),
usbip_exported_device_delete);
if (!host_driver->edev_list) {
dbg("dlist_new_with_delete failed");
goto err_free_host_driver;
}
host_driver->sysfs_driver = open_sysfs_host_driver();
if (!host_driver->sysfs_driver)
goto err_destroy_edev_list;
rc = refresh_exported_devices();
if (rc < 0)
goto err_close_sysfs_driver;
return 0;
err_close_sysfs_driver:
sysfs_close_driver(host_driver->sysfs_driver);
err_destroy_edev_list:
dlist_destroy(host_driver->edev_list);
err_free_host_driver:
free(host_driver);
host_driver = NULL;
return -1;
}
void usbip_host_driver_close(void)
{
if (!host_driver)
return;
if (host_driver->edev_list)
dlist_destroy(host_driver->edev_list);
if (host_driver->sysfs_driver)
sysfs_close_driver(host_driver->sysfs_driver);
free(host_driver);
host_driver = NULL;
}
int usbip_host_refresh_device_list(void)
{
int rc;
if (host_driver->edev_list)
dlist_destroy(host_driver->edev_list);
host_driver->ndevs = 0;
host_driver->edev_list =
dlist_new_with_delete(sizeof(struct usbip_exported_device),
usbip_exported_device_delete);
if (!host_driver->edev_list) {
dbg("dlist_new_with_delete failed");
return -1;
}
rc = refresh_exported_devices();
if (rc < 0)
return -1;
return 0;
}
int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd)
{
char attr_name[] = "usbip_sockfd";
char attr_path[SYSFS_PATH_MAX];
struct sysfs_attribute *attr;
char sockfd_buff[30];
int ret;
if (edev->status != SDEV_ST_AVAILABLE) {
dbg("device not available: %s", edev->udev.busid);
switch (edev->status) {
case SDEV_ST_ERROR:
dbg("status SDEV_ST_ERROR");
break;
case SDEV_ST_USED:
dbg("status SDEV_ST_USED");
break;
default:
dbg("status unknown: 0x%x", edev->status);
}
return -1;
}
/* only the first interface is true */
snprintf(attr_path, sizeof(attr_path), "%s/%s:%d.%d/%s",
edev->udev.path, edev->udev.busid,
edev->udev.bConfigurationValue, 0, attr_name);
attr = sysfs_open_attribute(attr_path);
if (!attr) {
dbg("sysfs_open_attribute failed: %s", attr_path);
return -1;
}
snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd);
dbg("write: %s", sockfd_buff);
ret = sysfs_write_attribute(attr, sockfd_buff, strlen(sockfd_buff));
if (ret < 0) {
dbg("sysfs_write_attribute failed: sockfd %s to %s",
sockfd_buff, attr_path);
goto err_write_sockfd;
}
dbg("connect: %s", edev->udev.busid);
err_write_sockfd:
sysfs_close_attribute(attr);
return ret;
}
struct usbip_exported_device *usbip_host_get_device(int num)
{
struct usbip_exported_device *edev;
struct dlist *dlist = host_driver->edev_list;
int cnt = 0;
dlist_for_each_data(dlist, edev, struct usbip_exported_device) {
if (num == cnt)
return edev;
else
cnt++;
}
return NULL;
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBIP_HOST_DRIVER_H
#define __USBIP_HOST_DRIVER_H
#include <stdint.h>
#include "usbip_common.h"
struct usbip_host_driver {
int ndevs;
struct sysfs_driver *sysfs_driver;
/* list of exported device */
struct dlist *edev_list;
};
struct usbip_exported_device {
struct sysfs_device *sudev;
int32_t status;
struct usbip_usb_device udev;
struct usbip_usb_interface uinf[];
};
extern struct usbip_host_driver *host_driver;
int usbip_host_driver_open(void);
void usbip_host_driver_close(void);
int usbip_host_refresh_device_list(void);
int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd);
struct usbip_exported_device *usbip_host_get_device(int num);
#endif /* __USBIP_HOST_DRIVER_H */

View File

@@ -0,0 +1,537 @@
/*
* Copyright (C) 2005-2007 Takahiro Hirofuchi
*/
#include "usbip_common.h"
#include "vhci_driver.h"
#undef PROGNAME
#define PROGNAME "libusbip"
struct usbip_vhci_driver *vhci_driver;
static struct usbip_imported_device *
imported_device_init(struct usbip_imported_device *idev, char *busid)
{
struct sysfs_device *sudev;
sudev = sysfs_open_device("usb", busid);
if (!sudev) {
dbg("sysfs_open_device failed: %s", busid);
goto err;
}
read_usb_device(sudev, &idev->udev);
sysfs_close_device(sudev);
/* add class devices of this imported device */
struct usbip_class_device *cdev;
dlist_for_each_data(vhci_driver->cdev_list, cdev,
struct usbip_class_device) {
if (!strncmp(cdev->dev_path, idev->udev.path,
strlen(idev->udev.path))) {
struct usbip_class_device *new_cdev;
/*
* alloc and copy because dlist is linked
* from only one list
*/
new_cdev = calloc(1, sizeof(*new_cdev));
if (!new_cdev)
goto err;
memcpy(new_cdev, cdev, sizeof(*new_cdev));
dlist_unshift(idev->cdev_list, (void *) new_cdev);
}
}
return idev;
err:
return NULL;
}
static int parse_status(char *value)
{
int ret = 0;
char *c;
for (int i = 0; i < vhci_driver->nports; i++)
memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i]));
/* skip a header line */
c = strchr(value, '\n');
if (!c)
return -1;
c++;
while (*c != '\0') {
int port, status, speed, devid;
unsigned long socket;
char lbusid[SYSFS_BUS_ID_SIZE];
ret = sscanf(c, "%d %d %d %x %lx %s\n",
&port, &status, &speed,
&devid, &socket, lbusid);
if (ret < 5) {
dbg("sscanf failed: %d", ret);
BUG();
}
dbg("port %d status %d speed %d devid %x",
port, status, speed, devid);
dbg("socket %lx lbusid %s", socket, lbusid);
/* if a device is connected, look at it */
{
struct usbip_imported_device *idev = &vhci_driver->idev[port];
idev->port = port;
idev->status = status;
idev->devid = devid;
idev->busnum = (devid >> 16);
idev->devnum = (devid & 0x0000ffff);
idev->cdev_list = dlist_new(sizeof(struct usbip_class_device));
if (!idev->cdev_list) {
dbg("dlist_new failed");
return -1;
}
if (idev->status != VDEV_ST_NULL
&& idev->status != VDEV_ST_NOTASSIGNED) {
idev = imported_device_init(idev, lbusid);
if (!idev) {
dbg("imported_device_init failed");
return -1;
}
}
}
/* go to the next line */
c = strchr(c, '\n');
if (!c)
break;
c++;
}
dbg("exit");
return 0;
}
static int check_usbip_device(struct sysfs_class_device *cdev)
{
/* /sys/class/video4linux/video0/device */
char class_path[SYSFS_PATH_MAX];
/* /sys/devices/platform/vhci_hcd/usb6/6-1:1.1 */
char dev_path[SYSFS_PATH_MAX];
int ret;
struct usbip_class_device *usbip_cdev;
snprintf(class_path, sizeof(class_path), "%s/device", cdev->path);
ret = sysfs_get_link(class_path, dev_path, sizeof(dev_path));
if (ret == 0) {
if (!strncmp(dev_path, vhci_driver->hc_device->path,
strlen(vhci_driver->hc_device->path))) {
/* found usbip device */
usbip_cdev = calloc(1, sizeof(*usbip_cdev));
if (!usbip_cdev) {
dbg("calloc failed");
return -1;
}
dlist_unshift(vhci_driver->cdev_list, usbip_cdev);
strncpy(usbip_cdev->class_path, class_path,
sizeof(usbip_cdev->class_path));
strncpy(usbip_cdev->dev_path, dev_path,
sizeof(usbip_cdev->dev_path));
dbg("found: %s %s", class_path, dev_path);
}
}
return 0;
}
static int search_class_for_usbip_device(char *cname)
{
struct sysfs_class *class;
struct dlist *cdev_list;
struct sysfs_class_device *cdev;
int ret = 0;
class = sysfs_open_class(cname);
if (!class) {
dbg("sysfs_open_class failed");
return -1;
}
dbg("class: %s", class->name);
cdev_list = sysfs_get_class_devices(class);
if (!cdev_list)
/* nothing */
goto out;
dlist_for_each_data(cdev_list, cdev, struct sysfs_class_device) {
dbg("cdev: %s", cdev->name);
ret = check_usbip_device(cdev);
if (ret < 0)
goto out;
}
out:
sysfs_close_class(class);
return ret;
}
static int refresh_class_device_list(void)
{
int ret;
struct dlist *cname_list;
char *cname;
char sysfs_mntpath[SYSFS_PATH_MAX];
char class_path[SYSFS_PATH_MAX];
ret = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
if (ret < 0) {
dbg("sysfs_get_mnt_path failed");
return -1;
}
snprintf(class_path, sizeof(class_path), "%s/%s", sysfs_mntpath,
SYSFS_CLASS_NAME);
/* search under /sys/class */
cname_list = sysfs_open_directory_list(class_path);
if (!cname_list) {
dbg("sysfs_open_directory failed");
return -1;
}
dlist_for_each_data(cname_list, cname, char) {
ret = search_class_for_usbip_device(cname);
if (ret < 0) {
sysfs_close_list(cname_list);
return -1;
}
}
sysfs_close_list(cname_list);
/* seach under /sys/block */
ret = search_class_for_usbip_device(SYSFS_BLOCK_NAME);
if (ret < 0)
return -1;
return 0;
}
static int refresh_imported_device_list(void)
{
struct sysfs_attribute *attr_status;
attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status");
if (!attr_status) {
dbg("sysfs_get_device_attr(\"status\") failed: %s",
vhci_driver->hc_device->name);
return -1;
}
dbg("name: %s path: %s len: %d method: %d value: %s",
attr_status->name, attr_status->path, attr_status->len,
attr_status->method, attr_status->value);
return parse_status(attr_status->value);
}
static int get_nports(void)
{
char *c;
int nports = 0;
struct sysfs_attribute *attr_status;
attr_status = sysfs_get_device_attr(vhci_driver->hc_device, "status");
if (!attr_status) {
dbg("sysfs_get_device_attr(\"status\") failed: %s",
vhci_driver->hc_device->name);
return -1;
}
dbg("name: %s path: %s len: %d method: %d value: %s",
attr_status->name, attr_status->path, attr_status->len,
attr_status->method, attr_status->value);
/* skip a header line */
c = strchr(attr_status->value, '\n');
if (!c)
return 0;
c++;
while (*c != '\0') {
/* go to the next line */
c = strchr(c, '\n');
if (!c)
return nports;
c++;
nports += 1;
}
return nports;
}
static int get_hc_busid(char *sysfs_mntpath, char *hc_busid)
{
struct sysfs_driver *sdriver;
char sdriver_path[SYSFS_PATH_MAX];
struct sysfs_device *hc_dev;
struct dlist *hc_devs;
int found = 0;
snprintf(sdriver_path, SYSFS_PATH_MAX, "%s/%s/%s/%s/%s", sysfs_mntpath,
SYSFS_BUS_NAME, USBIP_VHCI_BUS_TYPE, SYSFS_DRIVERS_NAME,
USBIP_VHCI_DRV_NAME);
sdriver = sysfs_open_driver_path(sdriver_path);
if (!sdriver) {
dbg("sysfs_open_driver_path failed: %s", sdriver_path);
dbg("make sure " USBIP_CORE_MOD_NAME ".ko and "
USBIP_VHCI_DRV_NAME ".ko are loaded!");
return -1;
}
hc_devs = sysfs_get_driver_devices(sdriver);
if (!hc_devs) {
dbg("sysfs_get_driver failed");
goto err;
}
/* assume only one vhci_hcd */
dlist_for_each_data(hc_devs, hc_dev, struct sysfs_device) {
strncpy(hc_busid, hc_dev->bus_id, SYSFS_BUS_ID_SIZE);
found = 1;
}
err:
sysfs_close_driver(sdriver);
if (found)
return 0;
dbg("%s not found", hc_busid);
return -1;
}
/* ---------------------------------------------------------------------- */
int usbip_vhci_driver_open(void)
{
int ret;
char hc_busid[SYSFS_BUS_ID_SIZE];
vhci_driver = (struct usbip_vhci_driver *) calloc(1, sizeof(*vhci_driver));
if (!vhci_driver) {
dbg("calloc failed");
return -1;
}
ret = sysfs_get_mnt_path(vhci_driver->sysfs_mntpath, SYSFS_PATH_MAX);
if (ret < 0) {
dbg("sysfs_get_mnt_path failed");
goto err;
}
ret = get_hc_busid(vhci_driver->sysfs_mntpath, hc_busid);
if (ret < 0)
goto err;
/* will be freed in usbip_driver_close() */
vhci_driver->hc_device = sysfs_open_device(USBIP_VHCI_BUS_TYPE,
hc_busid);
if (!vhci_driver->hc_device) {
dbg("sysfs_open_device failed");
goto err;
}
vhci_driver->nports = get_nports();
dbg("available ports: %d", vhci_driver->nports);
vhci_driver->cdev_list = dlist_new(sizeof(struct usbip_class_device));
if (!vhci_driver->cdev_list)
goto err;
if (refresh_class_device_list())
goto err;
if (refresh_imported_device_list())
goto err;
return 0;
err:
if (vhci_driver->cdev_list)
dlist_destroy(vhci_driver->cdev_list);
if (vhci_driver->hc_device)
sysfs_close_device(vhci_driver->hc_device);
if (vhci_driver)
free(vhci_driver);
vhci_driver = NULL;
return -1;
}
void usbip_vhci_driver_close()
{
if (!vhci_driver)
return;
if (vhci_driver->cdev_list)
dlist_destroy(vhci_driver->cdev_list);
for (int i = 0; i < vhci_driver->nports; i++) {
if (vhci_driver->idev[i].cdev_list)
dlist_destroy(vhci_driver->idev[i].cdev_list);
}
if (vhci_driver->hc_device)
sysfs_close_device(vhci_driver->hc_device);
free(vhci_driver);
vhci_driver = NULL;
}
int usbip_vhci_refresh_device_list(void)
{
if (vhci_driver->cdev_list)
dlist_destroy(vhci_driver->cdev_list);
for (int i = 0; i < vhci_driver->nports; i++) {
if (vhci_driver->idev[i].cdev_list)
dlist_destroy(vhci_driver->idev[i].cdev_list);
}
vhci_driver->cdev_list = dlist_new(sizeof(struct usbip_class_device));
if (!vhci_driver->cdev_list)
goto err;
if (refresh_class_device_list())
goto err;
if (refresh_imported_device_list())
goto err;
return 0;
err:
if (vhci_driver->cdev_list)
dlist_destroy(vhci_driver->cdev_list);
for (int i = 0; i < vhci_driver->nports; i++) {
if (vhci_driver->idev[i].cdev_list)
dlist_destroy(vhci_driver->idev[i].cdev_list);
}
dbg("failed to refresh device list");
return -1;
}
int usbip_vhci_get_free_port(void)
{
for (int i = 0; i < vhci_driver->nports; i++) {
if (vhci_driver->idev[i].status == VDEV_ST_NULL)
return i;
}
return -1;
}
int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
uint32_t speed) {
struct sysfs_attribute *attr_attach;
char buff[200]; /* what size should be ? */
int ret;
attr_attach = sysfs_get_device_attr(vhci_driver->hc_device, "attach");
if (!attr_attach) {
dbg("sysfs_get_device_attr(\"attach\") failed: %s",
vhci_driver->hc_device->name);
return -1;
}
snprintf(buff, sizeof(buff), "%u %u %u %u",
port, sockfd, devid, speed);
dbg("writing: %s", buff);
ret = sysfs_write_attribute(attr_attach, buff, strlen(buff));
if (ret < 0) {
dbg("sysfs_write_attribute failed");
return -1;
}
dbg("attached port: %d", port);
return 0;
}
static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
{
return (busnum << 16) | devnum;
}
/* will be removed */
int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
uint8_t devnum, uint32_t speed)
{
int devid = get_devid(busnum, devnum);
return usbip_vhci_attach_device2(port, sockfd, devid, speed);
}
int usbip_vhci_detach_device(uint8_t port)
{
struct sysfs_attribute *attr_detach;
char buff[200]; /* what size should be ? */
int ret;
attr_detach = sysfs_get_device_attr(vhci_driver->hc_device, "detach");
if (!attr_detach) {
dbg("sysfs_get_device_attr(\"detach\") failed: %s",
vhci_driver->hc_device->name);
return -1;
}
snprintf(buff, sizeof(buff), "%u", port);
dbg("writing: %s", buff);
ret = sysfs_write_attribute(attr_detach, buff, strlen(buff));
if (ret < 0) {
dbg("sysfs_write_attribute failed");
return -1;
}
dbg("detached port: %d", port);
return 0;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2005-2007 Takahiro Hirofuchi
*/
#ifndef __VHCI_DRIVER_H
#define __VHCI_DRIVER_H
#include <sysfs/libsysfs.h>
#include <stdint.h>
#include "usbip_common.h"
#define USBIP_VHCI_BUS_TYPE "platform"
#define MAXNPORT 128
struct usbip_class_device {
char class_path[SYSFS_PATH_MAX];
char dev_path[SYSFS_PATH_MAX];
};
struct usbip_imported_device {
uint8_t port;
uint32_t status;
uint32_t devid;
uint8_t busnum;
uint8_t devnum;
/* usbip_class_device list */
struct dlist *cdev_list;
struct usbip_usb_device udev;
};
struct usbip_vhci_driver {
char sysfs_mntpath[SYSFS_PATH_MAX];
/* /sys/devices/platform/vhci_hcd */
struct sysfs_device *hc_device;
/* usbip_class_device list */
struct dlist *cdev_list;
int nports;
struct usbip_imported_device idev[MAXNPORT];
};
extern struct usbip_vhci_driver *vhci_driver;
int usbip_vhci_driver_open(void);
void usbip_vhci_driver_close(void);
int usbip_vhci_refresh_device_list(void);
int usbip_vhci_get_free_port(void);
int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid,
uint32_t speed);
/* will be removed */
int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum,
uint8_t devnum, uint32_t speed);
int usbip_vhci_detach_device(uint8_t port);
#endif /* __VHCI_DRIVER_H */

View File

@@ -0,0 +1,12 @@
AM_CPPFLAGS = -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"'
AM_CFLAGS = @EXTRA_CFLAGS@
LDADD = $(top_builddir)/libsrc/libusbip.la
sbin_PROGRAMS := usbip usbipd
usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \
usbip_attach.c usbip_detach.c usbip_list.c \
usbip_bind.c usbip_unbind.c
usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c

View File

@@ -0,0 +1,190 @@
/*
* command structure borrowed from udev
* (git://git.kernel.org/pub/scm/linux/hotplug/udev.git)
*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <syslog.h>
#include "usbip_common.h"
#include "usbip.h"
static int usbip_help(int argc, char *argv[]);
static int usbip_version(int argc, char *argv[]);
static const char usbip_version_string[] = PACKAGE_STRING;
static const char usbip_usage_string[] =
"usbip [--debug] [--log] [version]\n"
" [help] <command> <args>\n";
static void usbip_usage(void)
{
printf("usage: %s", usbip_usage_string);
}
struct command {
const char *name;
int (*fn)(int argc, char *argv[]);
const char *help;
void (*usage)(void);
};
static const struct command cmds[] = {
{
.name = "help",
.fn = usbip_help,
.help = NULL,
.usage = NULL
},
{
.name = "version",
.fn = usbip_version,
.help = NULL,
.usage = NULL
},
{
.name = "attach",
.fn = usbip_attach,
.help = "Attach a remote USB device",
.usage = usbip_attach_usage
},
{
.name = "detach",
.fn = usbip_detach,
.help = "Detach a remote USB device",
.usage = usbip_detach_usage
},
{
.name = "list",
.fn = usbip_list,
.help = "List exportable or local USB devices",
.usage = usbip_list_usage
},
{
.name = "bind",
.fn = usbip_bind,
.help = "Bind device to " USBIP_HOST_DRV_NAME ".ko",
.usage = usbip_bind_usage
},
{
.name = "unbind",
.fn = usbip_unbind,
.help = "Unbind device from " USBIP_HOST_DRV_NAME ".ko",
.usage = usbip_unbind_usage
},
{ NULL, NULL, NULL, NULL }
};
static int usbip_help(int argc, char *argv[])
{
const struct command *cmd;
int i;
int ret = 0;
if (argc > 1 && argv++) {
for (i = 0; cmds[i].name != NULL; i++)
if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) {
cmds[i].usage();
goto done;
}
ret = -1;
}
usbip_usage();
printf("\n");
for (cmd = cmds; cmd->name != NULL; cmd++)
if (cmd->help != NULL)
printf(" %-10s %s\n", cmd->name, cmd->help);
printf("\n");
done:
return ret;
}
static int usbip_version(int argc, char *argv[])
{
(void) argc;
(void) argv;
printf(PROGNAME " (%s)\n", usbip_version_string);
return 0;
}
static int run_command(const struct command *cmd, int argc, char *argv[])
{
dbg("running command: `%s'", cmd->name);
return cmd->fn(argc, argv);
}
int main(int argc, char *argv[])
{
static const struct option opts[] = {
{ "debug", no_argument, NULL, 'd' },
{ "log", no_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 }
};
char *cmd;
int opt;
int i, rc = -1;
usbip_use_stderr = 1;
opterr = 0;
for (;;) {
opt = getopt_long(argc, argv, "+d", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'd':
usbip_use_debug = 1;
break;
case 'l':
usbip_use_syslog = 1;
openlog("", LOG_PID, LOG_USER);
break;
case '?':
printf("usbip: invalid option\n");
default:
usbip_usage();
goto out;
}
}
cmd = argv[optind];
if (cmd) {
for (i = 0; cmds[i].name != NULL; i++)
if (!strcmp(cmds[i].name, cmd)) {
argc -= optind;
argv += optind;
optind = 0;
rc = run_command(&cmds[i], argc, argv);
goto out;
}
}
/* invalid command */
usbip_help(0, NULL);
out:
return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USBIP_H
#define __USBIP_H
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
/* usbip commands */
int usbip_attach(int argc, char *argv[]);
int usbip_detach(int argc, char *argv[]);
int usbip_list(int argc, char *argv[]);
int usbip_bind(int argc, char *argv[]);
int usbip_unbind(int argc, char *argv[]);
void usbip_attach_usage(void);
void usbip_detach_usage(void);
void usbip_list_usage(void);
void usbip_bind_usage(void);
void usbip_unbind_usage(void);
#endif /* __USBIP_H */

View File

@@ -0,0 +1,241 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.h>
#include <sysfs/libsysfs.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <unistd.h>
#include <errno.h>
#include "vhci_driver.h"
#include "usbip_common.h"
#include "usbip_network.h"
#include "usbip.h"
static const char usbip_attach_usage_string[] =
"usbip attach <args>\n"
" -r, --remote=<host> The machine with exported USB devices\n"
" -b, --busid=<busid> Busid of the device on <host>\n";
void usbip_attach_usage(void)
{
printf("usage: %s", usbip_attach_usage_string);
}
#define MAX_BUFF 100
static int record_connection(char *host, char *port, char *busid, int rhport)
{
int fd;
char path[PATH_MAX+1];
char buff[MAX_BUFF+1];
int ret;
ret = mkdir(VHCI_STATE_PATH, 0700);
if (ret < 0) {
/* if VHCI_STATE_PATH exists, then it better be a directory */
if (errno == EEXIST) {
struct stat s;
ret = stat(VHCI_STATE_PATH, &s);
if (ret < 0)
return -1;
if (!(s.st_mode & S_IFDIR))
return -1;
} else
return -1;
}
snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
if (fd < 0)
return -1;
snprintf(buff, MAX_BUFF, "%s %s %s\n",
host, port, busid);
ret = write(fd, buff, strlen(buff));
if (ret != (ssize_t) strlen(buff)) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static int import_device(int sockfd, struct usbip_usb_device *udev)
{
int rc;
int port;
rc = usbip_vhci_driver_open();
if (rc < 0) {
err("open vhci_driver");
return -1;
}
port = usbip_vhci_get_free_port();
if (port < 0) {
err("no free port");
usbip_vhci_driver_close();
return -1;
}
rc = usbip_vhci_attach_device(port, sockfd, udev->busnum,
udev->devnum, udev->speed);
if (rc < 0) {
err("import device");
usbip_vhci_driver_close();
return -1;
}
usbip_vhci_driver_close();
return port;
}
static int query_import_device(int sockfd, char *busid)
{
int rc;
struct op_import_request request;
struct op_import_reply reply;
uint16_t code = OP_REP_IMPORT;
memset(&request, 0, sizeof(request));
memset(&reply, 0, sizeof(reply));
/* send a request */
rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0);
if (rc < 0) {
err("send op_common");
return -1;
}
strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1);
PACK_OP_IMPORT_REQUEST(0, &request);
rc = usbip_net_send(sockfd, (void *) &request, sizeof(request));
if (rc < 0) {
err("send op_import_request");
return -1;
}
/* recieve a reply */
rc = usbip_net_recv_op_common(sockfd, &code);
if (rc < 0) {
err("recv op_common");
return -1;
}
rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply));
if (rc < 0) {
err("recv op_import_reply");
return -1;
}
PACK_OP_IMPORT_REPLY(0, &reply);
/* check the reply */
if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) {
err("recv different busid %s", reply.udev.busid);
return -1;
}
/* import a device */
return import_device(sockfd, &reply.udev);
}
static int attach_device(char *host, char *busid)
{
int sockfd;
int rc;
int rhport;
sockfd = usbip_net_tcp_connect(host, USBIP_PORT_STRING);
if (sockfd < 0) {
err("tcp connect");
return -1;
}
rhport = query_import_device(sockfd, busid);
if (rhport < 0) {
err("query");
return -1;
}
close(sockfd);
rc = record_connection(host, USBIP_PORT_STRING, busid, rhport);
if (rc < 0) {
err("record connection");
return -1;
}
return 0;
}
int usbip_attach(int argc, char *argv[])
{
static const struct option opts[] = {
{ "remote", required_argument, NULL, 'r' },
{ "busid", required_argument, NULL, 'b' },
{ NULL, 0, NULL, 0 }
};
char *host = NULL;
char *busid = NULL;
int opt;
int ret = -1;
for (;;) {
opt = getopt_long(argc, argv, "r:b:", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'r':
host = optarg;
break;
case 'b':
busid = optarg;
break;
default:
goto err_out;
}
}
if (!host || !busid)
goto err_out;
ret = attach_device(host, busid);
goto out;
err_out:
usbip_attach_usage();
out:
return ret;
}

View File

@@ -0,0 +1,277 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sysfs/libsysfs.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "usbip_common.h"
#include "utils.h"
#include "usbip.h"
enum unbind_status {
UNBIND_ST_OK,
UNBIND_ST_USBIP_HOST,
UNBIND_ST_FAILED
};
static const char usbip_bind_usage_string[] =
"usbip bind <args>\n"
" -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device "
"on <busid>\n";
void usbip_bind_usage(void)
{
printf("usage: %s", usbip_bind_usage_string);
}
/* call at unbound state */
static int bind_usbip(char *busid)
{
char bus_type[] = "usb";
char attr_name[] = "bind";
char sysfs_mntpath[SYSFS_PATH_MAX];
char bind_attr_path[SYSFS_PATH_MAX];
char intf_busid[SYSFS_BUS_ID_SIZE];
struct sysfs_device *busid_dev;
struct sysfs_attribute *bind_attr;
struct sysfs_attribute *bConfValue;
struct sysfs_attribute *bNumIntfs;
int i, failed = 0;
int rc, ret = -1;
rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
if (rc < 0) {
err("sysfs must be mounted: %s", strerror(errno));
return -1;
}
snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
sysfs_mntpath, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
USBIP_HOST_DRV_NAME, attr_name);
bind_attr = sysfs_open_attribute(bind_attr_path);
if (!bind_attr) {
dbg("problem getting bind attribute: %s", strerror(errno));
return -1;
}
busid_dev = sysfs_open_device(bus_type, busid);
if (!busid_dev) {
dbg("sysfs_open_device %s failed: %s", busid, strerror(errno));
goto err_close_bind_attr;
}
bConfValue = sysfs_get_device_attr(busid_dev, "bConfigurationValue");
bNumIntfs = sysfs_get_device_attr(busid_dev, "bNumInterfaces");
if (!bConfValue || !bNumIntfs) {
dbg("problem getting device attributes: %s",
strerror(errno));
goto err_close_busid_dev;
}
for (i = 0; i < atoi(bNumIntfs->value); i++) {
snprintf(intf_busid, SYSFS_BUS_ID_SIZE, "%s:%.1s.%d", busid,
bConfValue->value, i);
rc = sysfs_write_attribute(bind_attr, intf_busid,
SYSFS_BUS_ID_SIZE);
if (rc < 0) {
dbg("bind driver at %s failed", intf_busid);
failed = 1;
}
}
if (!failed)
ret = 0;
err_close_busid_dev:
sysfs_close_device(busid_dev);
err_close_bind_attr:
sysfs_close_attribute(bind_attr);
return ret;
}
/* buggy driver may cause dead lock */
static int unbind_other(char *busid)
{
char bus_type[] = "usb";
char intf_busid[SYSFS_BUS_ID_SIZE];
struct sysfs_device *busid_dev;
struct sysfs_device *intf_dev;
struct sysfs_driver *intf_drv;
struct sysfs_attribute *unbind_attr;
struct sysfs_attribute *bConfValue;
struct sysfs_attribute *bDevClass;
struct sysfs_attribute *bNumIntfs;
int i, rc;
enum unbind_status status = UNBIND_ST_OK;
busid_dev = sysfs_open_device(bus_type, busid);
if (!busid_dev) {
dbg("sysfs_open_device %s failed: %s", busid, strerror(errno));
return -1;
}
bConfValue = sysfs_get_device_attr(busid_dev, "bConfigurationValue");
bDevClass = sysfs_get_device_attr(busid_dev, "bDeviceClass");
bNumIntfs = sysfs_get_device_attr(busid_dev, "bNumInterfaces");
if (!bConfValue || !bDevClass || !bNumIntfs) {
dbg("problem getting device attributes: %s",
strerror(errno));
goto err_close_busid_dev;
}
if (!strncmp(bDevClass->value, "09", bDevClass->len)) {
dbg("skip unbinding of hub");
goto err_close_busid_dev;
}
for (i = 0; i < atoi(bNumIntfs->value); i++) {
snprintf(intf_busid, SYSFS_BUS_ID_SIZE, "%s:%.1s.%d", busid,
bConfValue->value, i);
intf_dev = sysfs_open_device(bus_type, intf_busid);
if (!intf_dev) {
dbg("could not open interface device: %s",
strerror(errno));
goto err_close_busid_dev;
}
dbg("%s -> %s", intf_dev->name, intf_dev->driver_name);
if (!strncmp("unknown", intf_dev->driver_name, SYSFS_NAME_LEN))
/* unbound interface */
continue;
if (!strncmp(USBIP_HOST_DRV_NAME, intf_dev->driver_name,
SYSFS_NAME_LEN)) {
/* already bound to usbip-host */
status = UNBIND_ST_USBIP_HOST;
continue;
}
/* unbinding */
intf_drv = sysfs_open_driver(bus_type, intf_dev->driver_name);
if (!intf_drv) {
dbg("could not open interface driver on %s: %s",
intf_dev->name, strerror(errno));
goto err_close_intf_dev;
}
unbind_attr = sysfs_get_driver_attr(intf_drv, "unbind");
if (!unbind_attr) {
dbg("problem getting interface driver attribute: %s",
strerror(errno));
goto err_close_intf_drv;
}
rc = sysfs_write_attribute(unbind_attr, intf_dev->bus_id,
SYSFS_BUS_ID_SIZE);
if (rc < 0) {
/* NOTE: why keep unbinding other interfaces? */
dbg("unbind driver at %s failed", intf_dev->bus_id);
status = UNBIND_ST_FAILED;
}
sysfs_close_driver(intf_drv);
sysfs_close_device(intf_dev);
}
goto out;
err_close_intf_drv:
sysfs_close_driver(intf_drv);
err_close_intf_dev:
sysfs_close_device(intf_dev);
err_close_busid_dev:
status = UNBIND_ST_FAILED;
out:
sysfs_close_device(busid_dev);
return status;
}
static int bind_device(char *busid)
{
int rc;
rc = unbind_other(busid);
if (rc == UNBIND_ST_FAILED) {
err("could not unbind driver from device on busid %s", busid);
return -1;
} else if (rc == UNBIND_ST_USBIP_HOST) {
err("device on busid %s is already bound to %s", busid,
USBIP_HOST_DRV_NAME);
return -1;
}
rc = modify_match_busid(busid, 1);
if (rc < 0) {
err("unable to bind device on %s", busid);
return -1;
}
rc = bind_usbip(busid);
if (rc < 0) {
err("could not bind device to %s", USBIP_HOST_DRV_NAME);
modify_match_busid(busid, 0);
return -1;
}
printf("bind device on busid %s: complete\n", busid);
return 0;
}
int usbip_bind(int argc, char *argv[])
{
static const struct option opts[] = {
{ "busid", required_argument, NULL, 'b' },
{ NULL, 0, NULL, 0 }
};
int opt;
int ret = -1;
for (;;) {
opt = getopt_long(argc, argv, "b:", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'b':
ret = bind_device(optarg);
goto out;
default:
goto err_out;
}
}
err_out:
usbip_bind_usage();
out:
return ret;
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sysfs/libsysfs.h>
#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include "vhci_driver.h"
#include "usbip_common.h"
#include "usbip_network.h"
#include "usbip.h"
static const char usbip_detach_usage_string[] =
"usbip detach <args>\n"
" -p, --port=<port> " USBIP_VHCI_DRV_NAME
" port the device is on\n";
void usbip_detach_usage(void)
{
printf("usage: %s", usbip_detach_usage_string);
}
static int detach_port(char *port)
{
int ret;
uint8_t portnum;
char path[PATH_MAX+1];
for (unsigned int i = 0; i < strlen(port); i++)
if (!isdigit(port[i])) {
err("invalid port %s", port);
return -1;
}
/* check max port */
portnum = atoi(port);
/* remove the port state file */
snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum);
remove(path);
rmdir(VHCI_STATE_PATH);
ret = usbip_vhci_driver_open();
if (ret < 0) {
err("open vhci_driver");
return -1;
}
ret = usbip_vhci_detach_device(portnum);
if (ret < 0)
return -1;
usbip_vhci_driver_close();
return ret;
}
int usbip_detach(int argc, char *argv[])
{
static const struct option opts[] = {
{ "port", required_argument, NULL, 'p' },
{ NULL, 0, NULL, 0 }
};
int opt;
int ret = -1;
for (;;) {
opt = getopt_long(argc, argv, "p:", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'p':
ret = detach_port(optarg);
goto out;
default:
goto err_out;
}
}
err_out:
usbip_detach_usage();
out:
return ret;
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sysfs/libsysfs.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <netdb.h>
#include <unistd.h>
#include "usbip_common.h"
#include "usbip_network.h"
#include "usbip.h"
static const char usbip_list_usage_string[] =
"usbip list [-p|--parsable] <args>\n"
" -p, --parsable Parsable list format\n"
" -r, --remote=<host> List the exportable USB devices on <host>\n"
" -l, --local List the local USB devices\n";
void usbip_list_usage(void)
{
printf("usage: %s", usbip_list_usage_string);
}
static int get_exported_devices(char *host, int sockfd)
{
char product_name[100];
char class_name[100];
struct op_devlist_reply reply;
uint16_t code = OP_REP_DEVLIST;
struct usbip_usb_device udev;
struct usbip_usb_interface uintf;
unsigned int i;
int j, rc;
rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
if (rc < 0) {
dbg("usbip_net_send_op_common failed");
return -1;
}
rc = usbip_net_recv_op_common(sockfd, &code);
if (rc < 0) {
dbg("usbip_net_recv_op_common failed");
return -1;
}
memset(&reply, 0, sizeof(reply));
rc = usbip_net_recv(sockfd, &reply, sizeof(reply));
if (rc < 0) {
dbg("usbip_net_recv_op_devlist failed");
return -1;
}
PACK_OP_DEVLIST_REPLY(0, &reply);
dbg("exportable devices: %d\n", reply.ndev);
if (reply.ndev == 0) {
info("no exportable devices found on %s", host);
return 0;
}
printf("Exportable USB devices\n");
printf("======================\n");
printf(" - %s\n", host);
for (i = 0; i < reply.ndev; i++) {
memset(&udev, 0, sizeof(udev));
rc = usbip_net_recv(sockfd, &udev, sizeof(udev));
if (rc < 0) {
dbg("usbip_net_recv failed: usbip_usb_device[%d]", i);
return -1;
}
usbip_net_pack_usb_device(0, &udev);
usbip_names_get_product(product_name, sizeof(product_name),
udev.idVendor, udev.idProduct);
usbip_names_get_class(class_name, sizeof(class_name),
udev.bDeviceClass, udev.bDeviceSubClass,
udev.bDeviceProtocol);
printf("%11s: %s\n", udev.busid, product_name);
printf("%11s: %s\n", "", udev.path);
printf("%11s: %s\n", "", class_name);
for (j = 0; j < udev.bNumInterfaces; j++) {
rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf));
if (rc < 0) {
dbg("usbip_net_recv failed: usbip_usb_intf[%d]",
j);
return -1;
}
usbip_net_pack_usb_interface(0, &uintf);
usbip_names_get_class(class_name, sizeof(class_name),
uintf.bInterfaceClass,
uintf.bInterfaceSubClass,
uintf.bInterfaceProtocol);
printf("%11s: %2d - %s\n", "", j, class_name);
}
printf("\n");
}
return 0;
}
static int list_exported_devices(char *host)
{
int rc;
int sockfd;
sockfd = usbip_net_tcp_connect(host, USBIP_PORT_STRING);
if (sockfd < 0) {
err("could not connect to %s:%s: %s", host,
USBIP_PORT_STRING, gai_strerror(sockfd));
return -1;
}
dbg("connected to %s:%s", host, USBIP_PORT_STRING);
rc = get_exported_devices(host, sockfd);
if (rc < 0) {
err("failed to get device list from %s", host);
return -1;
}
close(sockfd);
return 0;
}
static void print_device(char *busid, char *vendor, char *product,
bool parsable)
{
if (parsable)
printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product);
else
printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product);
}
static void print_product_name(char *product_name, bool parsable)
{
if (!parsable)
printf(" %s\n", product_name);
}
static void print_interface(char *busid, char *driver, bool parsable)
{
if (parsable)
printf("%s=%s#", busid, driver);
else
printf("%9s%s -> %s\n", "", busid, driver);
}
static int is_device(void *x)
{
struct sysfs_attribute *devpath;
struct sysfs_device *dev = x;
int ret = 0;
devpath = sysfs_get_device_attr(dev, "devpath");
if (devpath && *devpath->value != '0')
ret = 1;
return ret;
}
static int devcmp(void *a, void *b)
{
return strcmp(a, b);
}
static int list_devices(bool parsable)
{
char bus_type[] = "usb";
char busid[SYSFS_BUS_ID_SIZE];
char product_name[128];
struct sysfs_bus *ubus;
struct sysfs_device *dev;
struct sysfs_device *intf;
struct sysfs_attribute *idVendor;
struct sysfs_attribute *idProduct;
struct sysfs_attribute *bConfValue;
struct sysfs_attribute *bNumIntfs;
struct dlist *devlist;
int i;
int ret = -1;
ubus = sysfs_open_bus(bus_type);
if (!ubus) {
err("could not open %s bus: %s", bus_type, strerror(errno));
return -1;
}
devlist = sysfs_get_bus_devices(ubus);
if (!devlist) {
err("could not get %s bus devices: %s", bus_type,
strerror(errno));
goto err_out;
}
/* remove interfaces and root hubs from device list */
dlist_filter_sort(devlist, is_device, devcmp);
if (!parsable) {
printf("Local USB devices\n");
printf("=================\n");
}
dlist_for_each_data(devlist, dev, struct sysfs_device) {
idVendor = sysfs_get_device_attr(dev, "idVendor");
idProduct = sysfs_get_device_attr(dev, "idProduct");
bConfValue = sysfs_get_device_attr(dev, "bConfigurationValue");
bNumIntfs = sysfs_get_device_attr(dev, "bNumInterfaces");
if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) {
err("problem getting device attributes: %s",
strerror(errno));
goto err_out;
}
/* get product name */
usbip_names_get_product(product_name, sizeof(product_name),
strtol(idVendor->value, NULL, 16),
strtol(idProduct->value, NULL, 16));
print_device(dev->bus_id, idVendor->value, idProduct->value,
parsable);
print_product_name(product_name, parsable);
for (i = 0; i < atoi(bNumIntfs->value); i++) {
snprintf(busid, sizeof(busid), "%s:%.1s.%d",
dev->bus_id, bConfValue->value, i);
intf = sysfs_open_device(bus_type, busid);
if (!intf) {
err("could not open device interface: %s",
strerror(errno));
goto err_out;
}
print_interface(busid, intf->driver_name, parsable);
sysfs_close_device(intf);
}
printf("\n");
}
ret = 0;
err_out:
sysfs_close_bus(ubus);
return ret;
}
int usbip_list(int argc, char *argv[])
{
static const struct option opts[] = {
{ "parsable", no_argument, NULL, 'p' },
{ "remote", required_argument, NULL, 'r' },
{ "local", no_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 }
};
bool parsable = false;
int opt;
int ret = -1;
if (usbip_names_init(USBIDS_FILE))
err("failed to open %s", USBIDS_FILE);
for (;;) {
opt = getopt_long(argc, argv, "pr:l", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'p':
parsable = true;
break;
case 'r':
ret = list_exported_devices(optarg);
goto out;
case 'l':
ret = list_devices(parsable);
goto out;
default:
goto err_out;
}
}
err_out:
usbip_list_usage();
out:
usbip_names_free();
return ret;
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include "usbip_common.h"
#include "usbip_network.h"
void usbip_net_pack_uint32_t(int pack, uint32_t *num)
{
uint32_t i;
if (pack)
i = htonl(*num);
else
i = ntohl(*num);
*num = i;
}
void usbip_net_pack_uint16_t(int pack, uint16_t *num)
{
uint16_t i;
if (pack)
i = htons(*num);
else
i = ntohs(*num);
*num = i;
}
void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev)
{
usbip_net_pack_uint32_t(pack, &udev->busnum);
usbip_net_pack_uint32_t(pack, &udev->devnum);
usbip_net_pack_uint32_t(pack, &udev->speed);
usbip_net_pack_uint16_t(pack, &udev->idVendor);
usbip_net_pack_uint16_t(pack, &udev->idProduct);
usbip_net_pack_uint16_t(pack, &udev->bcdDevice);
}
void usbip_net_pack_usb_interface(int pack __attribute__((unused)),
struct usbip_usb_interface *udev
__attribute__((unused)))
{
/* uint8_t members need nothing */
}
static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen,
int sending)
{
ssize_t nbytes;
ssize_t total = 0;
if (!bufflen)
return 0;
do {
if (sending)
nbytes = send(sockfd, buff, bufflen, 0);
else
nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL);
if (nbytes <= 0)
return -1;
buff = (void *)((intptr_t) buff + nbytes);
bufflen -= nbytes;
total += nbytes;
} while (bufflen > 0);
return total;
}
ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen)
{
return usbip_net_xmit(sockfd, buff, bufflen, 0);
}
ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen)
{
return usbip_net_xmit(sockfd, buff, bufflen, 1);
}
int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status)
{
struct op_common op_common;
int rc;
memset(&op_common, 0, sizeof(op_common));
op_common.version = USBIP_VERSION;
op_common.code = code;
op_common.status = status;
PACK_OP_COMMON(1, &op_common);
rc = usbip_net_send(sockfd, &op_common, sizeof(op_common));
if (rc < 0) {
dbg("usbip_net_send failed: %d", rc);
return -1;
}
return 0;
}
int usbip_net_recv_op_common(int sockfd, uint16_t *code)
{
struct op_common op_common;
int rc;
memset(&op_common, 0, sizeof(op_common));
rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common));
if (rc < 0) {
dbg("usbip_net_recv failed: %d", rc);
goto err;
}
PACK_OP_COMMON(0, &op_common);
if (op_common.version != USBIP_VERSION) {
dbg("version mismatch: %d %d", op_common.version,
USBIP_VERSION);
goto err;
}
switch (*code) {
case OP_UNSPEC:
break;
default:
if (op_common.code != *code) {
dbg("unexpected pdu %#0x for %#0x", op_common.code,
*code);
goto err;
}
}
if (op_common.status != ST_OK) {
dbg("request failed at peer: %d", op_common.status);
goto err;
}
*code = op_common.code;
return 0;
err:
return -1;
}
int usbip_net_set_reuseaddr(int sockfd)
{
const int val = 1;
int ret;
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
if (ret < 0)
dbg("setsockopt: SO_REUSEADDR");
return ret;
}
int usbip_net_set_nodelay(int sockfd)
{
const int val = 1;
int ret;
ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
if (ret < 0)
dbg("setsockopt: TCP_NODELAY");
return ret;
}
int usbip_net_set_keepalive(int sockfd)
{
const int val = 1;
int ret;
ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
if (ret < 0)
dbg("setsockopt: SO_KEEPALIVE");
return ret;
}
/*
* IPv6 Ready
*/
int usbip_net_tcp_connect(char *hostname, char *service)
{
struct addrinfo hints, *res, *rp;
int sockfd;
int ret;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* get all possible addresses */
ret = getaddrinfo(hostname, service, &hints, &res);
if (ret < 0) {
dbg("getaddrinfo: %s service %s: %s", hostname, service,
gai_strerror(ret));
return ret;
}
/* try the addresses */
for (rp = res; rp; rp = rp->ai_next) {
sockfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sockfd < 0)
continue;
/* should set TCP_NODELAY for usbip */
usbip_net_set_nodelay(sockfd);
/* TODO: write code for heartbeat */
usbip_net_set_keepalive(sockfd);
if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0)
break;
close(sockfd);
}
freeaddrinfo(res);
if (!rp)
return EAI_SYSTEM;
return sockfd;
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2005-2007 Takahiro Hirofuchi
*/
#ifndef __USBIP_NETWORK_H
#define __USBIP_NETWORK_H
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <sys/types.h>
#include <sysfs/libsysfs.h>
#include <stdint.h>
#define USBIP_PORT 3240
#define USBIP_PORT_STRING "3240"
/* ---------------------------------------------------------------------- */
/* Common header for all the kinds of PDUs. */
struct op_common {
uint16_t version;
#define OP_REQUEST (0x80 << 8)
#define OP_REPLY (0x00 << 8)
uint16_t code;
/* add more error code */
#define ST_OK 0x00
#define ST_NA 0x01
uint32_t status; /* op_code status (for reply) */
} __attribute__((packed));
#define PACK_OP_COMMON(pack, op_common) do {\
usbip_net_pack_uint16_t(pack, &(op_common)->version);\
usbip_net_pack_uint16_t(pack, &(op_common)->code);\
usbip_net_pack_uint32_t(pack, &(op_common)->status);\
} while (0)
/* ---------------------------------------------------------------------- */
/* Dummy Code */
#define OP_UNSPEC 0x00
#define OP_REQ_UNSPEC OP_UNSPEC
#define OP_REP_UNSPEC OP_UNSPEC
/* ---------------------------------------------------------------------- */
/* Retrieve USB device information. (still not used) */
#define OP_DEVINFO 0x02
#define OP_REQ_DEVINFO (OP_REQUEST | OP_DEVINFO)
#define OP_REP_DEVINFO (OP_REPLY | OP_DEVINFO)
struct op_devinfo_request {
char busid[SYSFS_BUS_ID_SIZE];
} __attribute__((packed));
struct op_devinfo_reply {
struct usbip_usb_device udev;
struct usbip_usb_interface uinf[];
} __attribute__((packed));
/* ---------------------------------------------------------------------- */
/* Import a remote USB device. */
#define OP_IMPORT 0x03
#define OP_REQ_IMPORT (OP_REQUEST | OP_IMPORT)
#define OP_REP_IMPORT (OP_REPLY | OP_IMPORT)
struct op_import_request {
char busid[SYSFS_BUS_ID_SIZE];
} __attribute__((packed));
struct op_import_reply {
struct usbip_usb_device udev;
// struct usbip_usb_interface uinf[];
} __attribute__((packed));
#define PACK_OP_IMPORT_REQUEST(pack, request) do {\
} while (0)
#define PACK_OP_IMPORT_REPLY(pack, reply) do {\
usbip_net_pack_usb_device(pack, &(reply)->udev);\
} while (0)
/* ---------------------------------------------------------------------- */
/* Export a USB device to a remote host. */
#define OP_EXPORT 0x06
#define OP_REQ_EXPORT (OP_REQUEST | OP_EXPORT)
#define OP_REP_EXPORT (OP_REPLY | OP_EXPORT)
struct op_export_request {
struct usbip_usb_device udev;
} __attribute__((packed));
struct op_export_reply {
int returncode;
} __attribute__((packed));
#define PACK_OP_EXPORT_REQUEST(pack, request) do {\
usbip_net_pack_usb_device(pack, &(request)->udev);\
} while (0)
#define PACK_OP_EXPORT_REPLY(pack, reply) do {\
} while (0)
/* ---------------------------------------------------------------------- */
/* un-Export a USB device from a remote host. */
#define OP_UNEXPORT 0x07
#define OP_REQ_UNEXPORT (OP_REQUEST | OP_UNEXPORT)
#define OP_REP_UNEXPORT (OP_REPLY | OP_UNEXPORT)
struct op_unexport_request {
struct usbip_usb_device udev;
} __attribute__((packed));
struct op_unexport_reply {
int returncode;
} __attribute__((packed));
#define PACK_OP_UNEXPORT_REQUEST(pack, request) do {\
usbip_net_pack_usb_device(pack, &(request)->udev);\
} while (0)
#define PACK_OP_UNEXPORT_REPLY(pack, reply) do {\
} while (0)
/* ---------------------------------------------------------------------- */
/* Negotiate IPSec encryption key. (still not used) */
#define OP_CRYPKEY 0x04
#define OP_REQ_CRYPKEY (OP_REQUEST | OP_CRYPKEY)
#define OP_REP_CRYPKEY (OP_REPLY | OP_CRYPKEY)
struct op_crypkey_request {
/* 128bit key */
uint32_t key[4];
} __attribute__((packed));
struct op_crypkey_reply {
uint32_t __reserved;
} __attribute__((packed));
/* ---------------------------------------------------------------------- */
/* Retrieve the list of exported USB devices. */
#define OP_DEVLIST 0x05
#define OP_REQ_DEVLIST (OP_REQUEST | OP_DEVLIST)
#define OP_REP_DEVLIST (OP_REPLY | OP_DEVLIST)
struct op_devlist_request {
} __attribute__((packed));
struct op_devlist_reply {
uint32_t ndev;
/* followed by reply_extra[] */
} __attribute__((packed));
struct op_devlist_reply_extra {
struct usbip_usb_device udev;
struct usbip_usb_interface uinf[];
} __attribute__((packed));
#define PACK_OP_DEVLIST_REQUEST(pack, request) do {\
} while (0)
#define PACK_OP_DEVLIST_REPLY(pack, reply) do {\
usbip_net_pack_uint32_t(pack, &(reply)->ndev);\
} while (0)
void usbip_net_pack_uint32_t(int pack, uint32_t *num);
void usbip_net_pack_uint16_t(int pack, uint16_t *num);
void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev);
void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf);
ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen);
ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen);
int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status);
int usbip_net_recv_op_common(int sockfd, uint16_t *code);
int usbip_net_set_reuseaddr(int sockfd);
int usbip_net_set_nodelay(int sockfd);
int usbip_net_set_keepalive(int sockfd);
int usbip_net_tcp_connect(char *hostname, char *port);
#endif /* __USBIP_NETWORK_H */

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sysfs/libsysfs.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include "usbip_common.h"
#include "utils.h"
#include "usbip.h"
static const char usbip_unbind_usage_string[] =
"usbip unbind <args>\n"
" -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from "
"device on <busid>\n";
void usbip_unbind_usage(void)
{
printf("usage: %s", usbip_unbind_usage_string);
}
static int unbind_device(char *busid)
{
char bus_type[] = "usb";
struct sysfs_driver *usbip_host_drv;
struct sysfs_device *dev;
struct dlist *devlist;
int verified = 0;
int rc, ret = -1;
char attr_name[] = "bConfigurationValue";
char sysfs_mntpath[SYSFS_PATH_MAX];
char busid_attr_path[SYSFS_PATH_MAX];
struct sysfs_attribute *busid_attr;
char *val = NULL;
int len;
/* verify the busid device is using usbip-host */
usbip_host_drv = sysfs_open_driver(bus_type, USBIP_HOST_DRV_NAME);
if (!usbip_host_drv) {
err("could not open %s driver: %s", USBIP_HOST_DRV_NAME,
strerror(errno));
return -1;
}
devlist = sysfs_get_driver_devices(usbip_host_drv);
if (!devlist) {
err("%s is not in use by any devices", USBIP_HOST_DRV_NAME);
goto err_close_usbip_host_drv;
}
dlist_for_each_data(devlist, dev, struct sysfs_device) {
if (!strncmp(busid, dev->name, strlen(busid)) &&
!strncmp(dev->driver_name, USBIP_HOST_DRV_NAME,
strlen(USBIP_HOST_DRV_NAME))) {
verified = 1;
break;
}
}
if (!verified) {
err("device on busid %s is not using %s", busid,
USBIP_HOST_DRV_NAME);
goto err_close_usbip_host_drv;
}
/*
* NOTE: A read and write of an attribute value of the device busid
* refers to must be done to start probing. That way a rebind of the
* default driver for the device occurs.
*
* This seems very hackish and adds a lot of pointless code. I think it
* should be done in the kernel by the driver after del_match_busid is
* finished!
*/
rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
if (rc < 0) {
err("sysfs must be mounted: %s", strerror(errno));
return -1;
}
snprintf(busid_attr_path, sizeof(busid_attr_path), "%s/%s/%s/%s/%s/%s",
sysfs_mntpath, SYSFS_BUS_NAME, bus_type, SYSFS_DEVICES_NAME,
busid, attr_name);
/* read a device attribute */
busid_attr = sysfs_open_attribute(busid_attr_path);
if (!busid_attr) {
err("could not open %s/%s: %s", busid, attr_name,
strerror(errno));
return -1;
}
if (sysfs_read_attribute(busid_attr) < 0) {
err("problem reading attribute: %s", strerror(errno));
goto err_out;
}
len = busid_attr->len;
val = malloc(len);
*val = *busid_attr->value;
sysfs_close_attribute(busid_attr);
/* notify driver of unbind */
rc = modify_match_busid(busid, 0);
if (rc < 0) {
err("unable to unbind device on %s", busid);
goto err_out;
}
/* write the device attribute */
busid_attr = sysfs_open_attribute(busid_attr_path);
if (!busid_attr) {
err("could not open %s/%s: %s", busid, attr_name,
strerror(errno));
return -1;
}
rc = sysfs_write_attribute(busid_attr, val, len);
if (rc < 0) {
err("problem writing attribute: %s", strerror(errno));
goto err_out;
}
sysfs_close_attribute(busid_attr);
ret = 0;
printf("unbind device on busid %s: complete\n", busid);
err_out:
free(val);
err_close_usbip_host_drv:
sysfs_close_driver(usbip_host_drv);
return ret;
}
int usbip_unbind(int argc, char *argv[])
{
static const struct option opts[] = {
{ "busid", required_argument, NULL, 'b' },
{ NULL, 0, NULL, 0 }
};
int opt;
int ret = -1;
for (;;) {
opt = getopt_long(argc, argv, "b:", opts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'b':
ret = unbind_device(optarg);
goto out;
default:
goto err_out;
}
}
err_out:
usbip_unbind_usage();
out:
return ret;
}

View File

@@ -0,0 +1,582 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#define _GNU_SOURCE
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
#endif
#include <getopt.h>
#include <signal.h>
#include <poll.h>
#include "usbip_host_driver.h"
#include "usbip_common.h"
#include "usbip_network.h"
#undef PROGNAME
#define PROGNAME "usbipd"
#define MAXSOCKFD 20
#define MAIN_LOOP_TIMEOUT 10
static const char usbip_version_string[] = PACKAGE_STRING;
static const char usbipd_help_string[] =
"usage: usbipd [options] \n"
" -D, --daemon \n"
" Run as a daemon process. \n"
" \n"
" -d, --debug \n"
" Print debugging information. \n"
" \n"
" -h, --help \n"
" Print this help. \n"
" \n"
" -v, --version \n"
" Show version. \n";
static void usbipd_help(void)
{
printf("%s\n", usbipd_help_string);
}
static int recv_request_import(int sockfd)
{
struct op_import_request req;
struct op_common reply;
struct usbip_exported_device *edev;
struct usbip_usb_device pdu_udev;
int found = 0;
int error = 0;
int rc;
memset(&req, 0, sizeof(req));
memset(&reply, 0, sizeof(reply));
rc = usbip_net_recv(sockfd, &req, sizeof(req));
if (rc < 0) {
dbg("usbip_net_recv failed: import request");
return -1;
}
PACK_OP_IMPORT_REQUEST(0, &req);
dlist_for_each_data(host_driver->edev_list, edev,
struct usbip_exported_device) {
if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
info("found requested device: %s", req.busid);
found = 1;
break;
}
}
if (found) {
/* should set TCP_NODELAY for usbip */
usbip_net_set_nodelay(sockfd);
/* export device needs a TCP/IP socket descriptor */
rc = usbip_host_export_device(edev, sockfd);
if (rc < 0)
error = 1;
} else {
info("requested device not found: %s", req.busid);
error = 1;
}
rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT,
(!error ? ST_OK : ST_NA));
if (rc < 0) {
dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT);
return -1;
}
if (error) {
dbg("import request busid %s: failed", req.busid);
return -1;
}
memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
usbip_net_pack_usb_device(1, &pdu_udev);
rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev));
if (rc < 0) {
dbg("usbip_net_send failed: devinfo");
return -1;
}
dbg("import request busid %s: complete", req.busid);
return 0;
}
static int send_reply_devlist(int connfd)
{
struct usbip_exported_device *edev;
struct usbip_usb_device pdu_udev;
struct usbip_usb_interface pdu_uinf;
struct op_devlist_reply reply;
int i;
int rc;
reply.ndev = 0;
/* number of exported devices */
dlist_for_each_data(host_driver->edev_list, edev,
struct usbip_exported_device) {
reply.ndev += 1;
}
info("exportable devices: %d", reply.ndev);
rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK);
if (rc < 0) {
dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST);
return -1;
}
PACK_OP_DEVLIST_REPLY(1, &reply);
rc = usbip_net_send(connfd, &reply, sizeof(reply));
if (rc < 0) {
dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST);
return -1;
}
dlist_for_each_data(host_driver->edev_list, edev,
struct usbip_exported_device) {
dump_usb_device(&edev->udev);
memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
usbip_net_pack_usb_device(1, &pdu_udev);
rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev));
if (rc < 0) {
dbg("usbip_net_send failed: pdu_udev");
return -1;
}
for (i = 0; i < edev->udev.bNumInterfaces; i++) {
dump_usb_interface(&edev->uinf[i]);
memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
usbip_net_pack_usb_interface(1, &pdu_uinf);
rc = usbip_net_send(connfd, &pdu_uinf,
sizeof(pdu_uinf));
if (rc < 0) {
dbg("usbip_net_send failed: pdu_uinf");
return -1;
}
}
}
return 0;
}
static int recv_request_devlist(int connfd)
{
struct op_devlist_request req;
int rc;
memset(&req, 0, sizeof(req));
rc = usbip_net_recv(connfd, &req, sizeof(req));
if (rc < 0) {
dbg("usbip_net_recv failed: devlist request");
return -1;
}
rc = send_reply_devlist(connfd);
if (rc < 0) {
dbg("send_reply_devlist failed");
return -1;
}
return 0;
}
static int recv_pdu(int connfd)
{
uint16_t code = OP_UNSPEC;
int ret;
ret = usbip_net_recv_op_common(connfd, &code);
if (ret < 0) {
dbg("could not receive opcode: %#0x", code);
return -1;
}
ret = usbip_host_refresh_device_list();
if (ret < 0) {
dbg("could not refresh device list: %d", ret);
return -1;
}
info("received request: %#0x(%d)", code, connfd);
switch (code) {
case OP_REQ_DEVLIST:
ret = recv_request_devlist(connfd);
break;
case OP_REQ_IMPORT:
ret = recv_request_import(connfd);
break;
case OP_REQ_DEVINFO:
case OP_REQ_CRYPKEY:
default:
err("received an unknown opcode: %#0x", code);
ret = -1;
}
if (ret == 0)
info("request %#0x(%d): complete", code, connfd);
else
info("request %#0x(%d): failed", code, connfd);
return ret;
}
#ifdef HAVE_LIBWRAP
static int tcpd_auth(int connfd)
{
struct request_info request;
int rc;
request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0);
fromhost(&request);
rc = hosts_access(&request);
if (rc == 0)
return -1;
return 0;
}
#endif
static int do_accept(int listenfd)
{
int connfd;
struct sockaddr_storage ss;
socklen_t len = sizeof(ss);
char host[NI_MAXHOST], port[NI_MAXSERV];
int rc;
memset(&ss, 0, sizeof(ss));
connfd = accept(listenfd, (struct sockaddr *) &ss, &len);
if (connfd < 0) {
err("failed to accept connection");
return -1;
}
rc = getnameinfo((struct sockaddr *) &ss, len, host, sizeof(host),
port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
if (rc)
err("getnameinfo: %s", gai_strerror(rc));
#ifdef HAVE_LIBWRAP
rc = tcpd_auth(connfd);
if (rc < 0) {
info("denied access from %s", host);
close(connfd);
return -1;
}
#endif
info("connection from %s:%s", host, port);
return connfd;
}
int process_request(int listenfd)
{
pid_t childpid;
int connfd;
connfd = do_accept(listenfd);
if (connfd < 0)
return -1;
childpid = fork();
if (childpid == 0) {
close(listenfd);
recv_pdu(connfd);
exit(0);
}
close(connfd);
return 0;
}
static void log_addrinfo(struct addrinfo *ai)
{
char hbuf[NI_MAXHOST];
char sbuf[NI_MAXSERV];
int rc;
rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
if (rc)
err("getnameinfo: %s", gai_strerror(rc));
info("listening on %s:%s", hbuf, sbuf);
}
static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[])
{
struct addrinfo *ai;
int ret, nsockfd = 0;
for (ai = ai_head; ai && nsockfd < MAXSOCKFD; ai = ai->ai_next) {
sockfdlist[nsockfd] = socket(ai->ai_family, ai->ai_socktype,
ai->ai_protocol);
if (sockfdlist[nsockfd] < 0)
continue;
usbip_net_set_reuseaddr(sockfdlist[nsockfd]);
usbip_net_set_nodelay(sockfdlist[nsockfd]);
if (sockfdlist[nsockfd] >= FD_SETSIZE) {
close(sockfdlist[nsockfd]);
sockfdlist[nsockfd] = -1;
continue;
}
ret = bind(sockfdlist[nsockfd], ai->ai_addr, ai->ai_addrlen);
if (ret < 0) {
close(sockfdlist[nsockfd]);
sockfdlist[nsockfd] = -1;
continue;
}
ret = listen(sockfdlist[nsockfd], SOMAXCONN);
if (ret < 0) {
close(sockfdlist[nsockfd]);
sockfdlist[nsockfd] = -1;
continue;
}
log_addrinfo(ai);
nsockfd++;
}
if (nsockfd == 0)
return -1;
dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es");
return nsockfd;
}
static struct addrinfo *do_getaddrinfo(char *host, int ai_family)
{
struct addrinfo hints, *ai_head;
int rc;
memset(&hints, 0, sizeof(hints));
hints.ai_family = ai_family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
rc = getaddrinfo(host, USBIP_PORT_STRING, &hints, &ai_head);
if (rc) {
err("failed to get a network address %s: %s", USBIP_PORT_STRING,
gai_strerror(rc));
return NULL;
}
return ai_head;
}
static void signal_handler(int i)
{
dbg("received '%s' signal", strsignal(i));
}
static void set_signal(void)
{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = signal_handler;
sigemptyset(&act.sa_mask);
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
act.sa_handler = SIG_IGN;
sigaction(SIGCLD, &act, NULL);
}
static int do_standalone_mode(int daemonize)
{
struct addrinfo *ai_head;
int sockfdlist[MAXSOCKFD];
int nsockfd;
int i, terminate;
struct pollfd *fds;
struct timespec timeout;
sigset_t sigmask;
if (usbip_host_driver_open()) {
err("please load " USBIP_CORE_MOD_NAME ".ko and "
USBIP_HOST_DRV_NAME ".ko!");
return -1;
}
if (daemonize) {
if (daemon(0, 0) < 0) {
err("daemonizing failed: %s", strerror(errno));
usbip_host_driver_close();
return -1;
}
umask(0);
usbip_use_syslog = 1;
}
set_signal();
ai_head = do_getaddrinfo(NULL, PF_UNSPEC);
if (!ai_head) {
usbip_host_driver_close();
return -1;
}
info("starting " PROGNAME " (%s)", usbip_version_string);
nsockfd = listen_all_addrinfo(ai_head, sockfdlist);
if (nsockfd <= 0) {
err("failed to open a listening socket");
freeaddrinfo(ai_head);
usbip_host_driver_close();
return -1;
}
fds = calloc(nsockfd, sizeof(struct pollfd));
for (i = 0; i < nsockfd; i++) {
fds[i].fd = sockfdlist[i];
fds[i].events = POLLIN;
}
timeout.tv_sec = MAIN_LOOP_TIMEOUT;
timeout.tv_nsec = 0;
sigfillset(&sigmask);
sigdelset(&sigmask, SIGTERM);
sigdelset(&sigmask, SIGINT);
terminate = 0;
while (!terminate) {
int r;
r = ppoll(fds, nsockfd, &timeout, &sigmask);
if (r < 0) {
dbg("%s", strerror(errno));
terminate = 1;
} else if (r) {
for (i = 0; i < nsockfd; i++) {
if (fds[i].revents & POLLIN) {
dbg("read event on fd[%d]=%d",
i, sockfdlist[i]);
process_request(sockfdlist[i]);
}
}
} else
dbg("heartbeat timeout on ppoll()");
}
info("shutting down " PROGNAME);
free(fds);
freeaddrinfo(ai_head);
usbip_host_driver_close();
return 0;
}
int main(int argc, char *argv[])
{
static const struct option longopts[] = {
{ "daemon", no_argument, NULL, 'D' },
{ "debug", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
enum {
cmd_standalone_mode = 1,
cmd_help,
cmd_version
} cmd;
int daemonize = 0;
int opt, rc = -1;
usbip_use_stderr = 1;
usbip_use_syslog = 0;
if (geteuid() != 0)
err("not running as root?");
cmd = cmd_standalone_mode;
for (;;) {
opt = getopt_long(argc, argv, "Ddhv", longopts, NULL);
if (opt == -1)
break;
switch (opt) {
case 'D':
daemonize = 1;
break;
case 'd':
usbip_use_debug = 1;
break;
case 'h':
cmd = cmd_help;
break;
case 'v':
cmd = cmd_version;
break;
case '?':
usbipd_help();
default:
goto err_out;
}
}
switch (cmd) {
case cmd_standalone_mode:
rc = do_standalone_mode(daemonize);
break;
case cmd_version:
printf(PROGNAME " (%s)\n", usbip_version_string);
rc = 0;
break;
case cmd_help:
usbipd_help();
rc = 0;
break;
default:
usbipd_help();
goto err_out;
}
err_out:
return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE);
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sysfs/libsysfs.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "usbip_common.h"
#include "utils.h"
int modify_match_busid(char *busid, int add)
{
char bus_type[] = "usb";
char attr_name[] = "match_busid";
char buff[SYSFS_BUS_ID_SIZE + 4];
char sysfs_mntpath[SYSFS_PATH_MAX];
char match_busid_attr_path[SYSFS_PATH_MAX];
struct sysfs_attribute *match_busid_attr;
int rc, ret = 0;
if (strnlen(busid, SYSFS_BUS_ID_SIZE) > SYSFS_BUS_ID_SIZE - 1) {
dbg("busid is too long");
return -1;
}
rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
if (rc < 0) {
err("sysfs must be mounted: %s", strerror(errno));
return -1;
}
snprintf(match_busid_attr_path, sizeof(match_busid_attr_path),
"%s/%s/%s/%s/%s/%s", sysfs_mntpath, SYSFS_BUS_NAME, bus_type,
SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name);
match_busid_attr = sysfs_open_attribute(match_busid_attr_path);
if (!match_busid_attr) {
dbg("problem getting match_busid attribute: %s",
strerror(errno));
return -1;
}
if (add)
snprintf(buff, SYSFS_BUS_ID_SIZE + 4, "add %s", busid);
else
snprintf(buff, SYSFS_BUS_ID_SIZE + 4, "del %s", busid);
dbg("write \"%s\" to %s", buff, match_busid_attr->path);
rc = sysfs_write_attribute(match_busid_attr, buff, sizeof(buff));
if (rc < 0) {
dbg("failed to write match_busid: %s", strerror(errno));
ret = -1;
}
sysfs_close_attribute(match_busid_attr);
return ret;
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
* 2005-2007 Takahiro Hirofuchi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __UTILS_H
#define __UTILS_H
int modify_match_busid(char *busid, int add);
#endif /* __UTILS_H */

View 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 */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,270 @@
/*
* 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");
return;
}
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;
}

View File

@@ -0,0 +1,248 @@
/*
* 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 show_status(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(status, S_IRUGO, show_status, NULL);
/* 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;
sscanf(buf, "%u", &rhport);
/* 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("speed %d\n", 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;
/*
* @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
*/
sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed);
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. */
/* The correct way to clean this up is to fput(socket->file). */
socket = sockfd_to_socket(sockfd);
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);
fput(socket->file);
dev_err(dev, "port %d already used\n", rhport);
return -EINVAL;
}
dev_info(dev, "rhport(%u) sockfd(%d) devid(%u) speed(%u)\n",
rhport, sockfd, devid, 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,
};

View 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;
}