mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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: Brian G. Merrell <bgmerrell@novell.com> Cc: Takahiro Hirofuchi <hirofuchi@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
		
							parent
							
								
									05a1f28e87
								
							
						
					
					
						commit
						04679b3489
					
				
					 7 changed files with 2171 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,9 @@
 | 
			
		|||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										142
									
								
								drivers/staging/usbip/vhci.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								drivers/staging/usbip/vhci.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,142 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1275
									
								
								drivers/staging/usbip/vhci_hcd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1275
									
								
								drivers/staging/usbip/vhci_hcd.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										251
									
								
								drivers/staging/usbip/vhci_rx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								drivers/staging/usbip/vhci_rx.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,251 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										250
									
								
								drivers/staging/usbip/vhci_sysfs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								drivers/staging/usbip/vhci_sysfs.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,250 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										239
									
								
								drivers/staging/usbip/vhci_tx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								drivers/staging/usbip/vhci_tx.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,239 @@
 | 
			
		|||
/*
 | 
			
		||||
 * 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");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in a new issue