Commit 04679b34 authored by Takahiro Hirofuchi's avatar Takahiro Hirofuchi Committed by Greg Kroah-Hartman

Staging: USB/IP: add client driver

This adds the USB IP client driver

Brian Merrell cleaned up a lot of this code and submitted it for
inclusion.  Greg also did a lot of cleanup.
Signed-off-by: default avatarBrian G. Merrell <bgmerrell@novell.com>
Cc: Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 05a1f28e
......@@ -12,3 +12,14 @@ config USB_IP_COMMON
module will be called usbip_common_mod.
If unsure, say N.
config USB_IP_VHCI_HCD
tristate "USB IP client driver"
depends on USB_IP_COMMON
default N
---help---
This enables the USB IP host controller driver which will
run on the client machine.
To compile this driver as a module, choose M here: the
module will be called vhci_hcd.
obj-$(CONFIG_USB_IP_COMMON) += usbip_common_mod.o
usbip_common_mod-objs := usbip_common.o usbip_event.o
obj-$(CONFIG_USB_IP_VHCI_HCD) += vhci-hcd.o
vhci-hcd-objs := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
/*
* 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/platform_device.h>
#include "../../usb/core/hcd.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;
};
/*
* The number of ports is less than 16 ?
* USB_MAXCHILDREN is statically defined to 16 in usb.h. Its maximum value
* would be 31 because the event_bits[1] of struct usb_hub is defined as
* unsigned long in hub.h
*/
#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];
/* vhci_device which has not been assiged its address yet */
int pending_port;
};
extern struct vhci_hcd *the_controller;
extern struct attribute_group dev_attr_group;
/*-------------------------------------------------------------------------*/
/* prototype declaration */
/* vhci_hcd.c */
void rh_port_connect(int rhport, enum usb_device_speed speed);
void rh_port_disconnect(int rhport);
void vhci_rx_loop(struct usbip_task *ut);
void vhci_tx_loop(struct usbip_task *ut);
#define hardware (&the_controller->pdev.dev)
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;
}
This diff is collapsed.
/*
* 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 "usbip_common.h"
#include "vhci.h"
/* get URB from transmitted urb queue */
static struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
__u32 seqnum)
{
struct vhci_priv *priv, *tmp;
struct urb *urb = NULL;
int status;
spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
if (priv->seqnum == seqnum) {
urb = priv->urb;
status = urb->status;
dbg_vhci_rx("find urb %p vurb %p seqnum %u\n",
urb, priv, seqnum);
/* TODO: fix logic here to improve indent situtation */
if (status != -EINPROGRESS) {
if (status == -ENOENT ||
status == -ECONNRESET)
dev_info(&urb->dev->dev,
"urb %p was unlinked "
"%ssynchronuously.\n", urb,
status == -ENOENT ? "" : "a");
else
dev_info(&urb->dev->dev,
"urb %p may be in a error, "
"status %d\n", urb, status);
}
list_del(&priv->list);
kfree(priv);
urb->hcpriv = NULL;
break;
}
}
spin_unlock(&vdev->priv_lock);
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;
urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum);
if (!urb) {
uerr("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
uinfo("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;
if (dbg_flag_vhci_rx)
usbip_dump_urb(urb);
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);
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) {
uinfo("unlink->seqnum %lu\n", unlink->seqnum);
if (unlink->seqnum == pdu->base.seqnum) {
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) {
uinfo("cannot find the pending unlink %u\n", pdu->base.seqnum);
return;
}
urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
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.
*/
uinfo("the urb (seqnum %d) was already given backed\n",
pdu->base.seqnum);
} else {
dbg_vhci_rx("now giveback urb %p\n", urb);
/* If unlink is succeed, status is -ECONNRESET */
urb->status = pdu->u.ret_unlink.status;
uinfo("%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);
return;
}
/* 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);
dbg_vhci_rx("Enter\n");
memset(&pdu, 0, sizeof(pdu));
/* 1. receive a pdu header */
ret = usbip_xmit(0, ud->tcp_socket, (char *) &pdu, sizeof(pdu), 0);
if (ret != sizeof(pdu)) {
uerr("receiving pdu failed! size is %d, should be %d\n",
ret, sizeof(pdu));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
return;
}
usbip_header_correct_endian(&pdu, 0);
if (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:
/* NOTREACHED */
uerr("unknown pdu %u\n", pdu.base.command);
usbip_dump_header(&pdu);
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
}
}
/*-------------------------------------------------------------------------*/
void vhci_rx_loop(struct usbip_task *ut)
{
struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_rx);
while (1) {
if (signal_pending(current)) {
dbg_vhci_rx("signal catched!\n");
break;
}
if (usbip_event_happend(ud))
break;
vhci_rx_pdu(ud);
}
}
/*
* 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 "usbip_common.h"
#include "vhci.h"
#include <linux/in.h>
/* TODO: refine locking ?*/
/* Sysfs entry to show port status */
static ssize_t show_status(struct device *dev, struct device_attribute *attr,
char *out)
{
char *s = out;
int i = 0;
if (!the_controller || !out)
BUG();
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", vdev->udev->dev.bus_id);
} 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;
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) {
uerr("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) {
uerr("invalid port %u\n", rhport);
return -EINVAL;
}
err = vhci_port_disconnect(rhport);
if (err < 0)
return -EINVAL;
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 < 0) || (rhport >= VHCI_NPORTS)) {
uerr("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_VARIABLE:
break;
default:
uerr("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);
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;
/* check sockfd */
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);
uerr("port %d already used\n", rhport);
return -EINVAL;
}
uinfo("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 */
/*
* this function will sleep, so should be out of the lock. but, it's ok
* because we already marked vdev as being used. really?
*/
usbip_start_threads(&vdev->ud);
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,
};
struct attribute_group dev_attr_group = {
.attrs = dev_attrs,
};
/*
* 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 "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;
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;
if (usb_pipein(urb->pipe))
pdup->base.direction = USBIP_DIR_IN;
else
pdup->base.direction = USBIP_DIR_OUT;
pdup->base.ep = usb_pipeendpoint(urb->pipe);
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)
{
unsigned long flags;
struct vhci_priv *priv, *tmp;
spin_lock_irqsave(&vdev->priv_lock, flags);
list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) {
list_move_tail(&priv->list, &vdev->priv_rx);
spin_unlock_irqrestore(&vdev->priv_lock, flags);
return priv;
}
spin_unlock_irqrestore(&vdev->priv_lock, flags);
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;
void *iso_buffer = NULL;
txsize = 0;
memset(&pdu_header, 0, sizeof(pdu_header));
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
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) {
uerr("sendmsg failed!, retval %d for %zd\n", ret,
txsize);
kfree(iso_buffer);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
return -1;
}
kfree(iso_buffer);
dbg_vhci_tx("send txdata\n");
total_size += txsize;
}
return total_size;
}
/*-------------------------------------------------------------------------*/
static struct vhci_unlink *dequeue_from_unlink_tx(struct vhci_device *vdev)
{
unsigned long flags;
struct vhci_unlink *unlink, *tmp;
spin_lock_irqsave(&vdev->priv_lock, flags);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
list_move_tail(&unlink->list, &vdev->unlink_rx);
spin_unlock_irqrestore(&vdev->priv_lock, flags);
return unlink;
}
spin_unlock_irqrestore(&vdev->priv_lock, flags);
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));
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) {
uerr("sendmsg failed!, retval %d for %zd\n", ret,
txsize);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP);
return -1;
}
dbg_vhci_tx("send txdata\n");
total_size += txsize;
}
return total_size;
}
/*-------------------------------------------------------------------------*/
void vhci_tx_loop(struct usbip_task *ut)
{
struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_tx);
struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
while (1) {
if (signal_pending(current)) {
uinfo("vhci_tx signal catched\n");
break;
}
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)));
dbg_vhci_tx("pending urbs ?, now wake up\n");
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment