aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/usbip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/usbip')
-rw-r--r--drivers/staging/usbip/stub.h1
-rw-r--r--drivers/staging/usbip/stub_dev.c18
-rw-r--r--drivers/staging/usbip/stub_rx.c4
-rw-r--r--drivers/staging/usbip/vhci.h6
-rw-r--r--drivers/staging/usbip/vhci_hcd.c54
-rw-r--r--drivers/staging/usbip/vhci_rx.c50
6 files changed, 108 insertions, 25 deletions
diff --git a/drivers/staging/usbip/stub.h b/drivers/staging/usbip/stub.h
index 30dbfb6d16f2..d73267961ef4 100644
--- a/drivers/staging/usbip/stub.h
+++ b/drivers/staging/usbip/stub.h
@@ -32,6 +32,7 @@
struct stub_device {
struct usb_interface *interface;
+ struct usb_device *udev;
struct list_head list;
struct usbip_device ud;
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index b186b5fed2b9..a7ce51cc8909 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -258,10 +258,11 @@ static void stub_shutdown_connection(struct usbip_device *ud)
static void stub_device_reset(struct usbip_device *ud)
{
struct stub_device *sdev = container_of(ud, struct stub_device, ud);
- struct usb_device *udev = interface_to_usbdev(sdev->interface);
+ struct usb_device *udev = sdev->udev;
int ret;
usbip_udbg("device reset");
+
ret = usb_lock_device_for_reset(udev, sdev->interface);
if (ret < 0) {
dev_err(&udev->dev, "lock for reset\n");
@@ -309,7 +310,8 @@ static void stub_device_unusable(struct usbip_device *ud)
*
* Allocates and initializes a new stub_device struct.
*/
-static struct stub_device *stub_device_alloc(struct usb_interface *interface)
+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);
@@ -324,7 +326,8 @@ static struct stub_device *stub_device_alloc(struct usb_interface *interface)
return NULL;
}
- sdev->interface = interface;
+ sdev->interface = usb_get_intf(interface);
+ sdev->udev = usb_get_dev(udev);
/*
* devid is defined with devnum when this driver is first allocated.
@@ -450,11 +453,12 @@ static int stub_probe(struct usb_interface *interface,
return err;
}
+ usb_get_intf(interface);
return 0;
}
/* ok. this is my device. */
- sdev = stub_device_alloc(interface);
+ sdev = stub_device_alloc(udev, interface);
if (!sdev)
return -ENOMEM;
@@ -476,6 +480,8 @@ static int stub_probe(struct usb_interface *interface,
dev_err(&interface->dev, "create sysfs files for %s\n",
udev_busid);
usb_set_intfdata(interface, NULL);
+ usb_put_intf(interface);
+
busid_priv->interf_count = 0;
busid_priv->sdev = NULL;
@@ -545,6 +551,7 @@ static void stub_disconnect(struct usb_interface *interface)
if (busid_priv->interf_count > 1) {
busid_priv->interf_count--;
shutdown_busid(busid_priv);
+ usb_put_intf(interface);
return;
}
@@ -554,6 +561,9 @@ static void stub_disconnect(struct usb_interface *interface)
/* 1. shutdown the current connection */
shutdown_busid(busid_priv);
+ usb_put_dev(sdev->udev);
+ usb_put_intf(interface);
+
/* 3. free sdev */
busid_priv->sdev = NULL;
stub_device_free(sdev);
diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c
index 3de6fd2539dc..ae6ac82754a4 100644
--- a/drivers/staging/usbip/stub_rx.c
+++ b/drivers/staging/usbip/stub_rx.c
@@ -364,7 +364,7 @@ static struct stub_priv *stub_priv_alloc(struct stub_device *sdev,
static int get_pipe(struct stub_device *sdev, int epnum, int dir)
{
- struct usb_device *udev = interface_to_usbdev(sdev->interface);
+ struct usb_device *udev = sdev->udev;
struct usb_host_endpoint *ep;
struct usb_endpoint_descriptor *epd = NULL;
@@ -484,7 +484,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
int ret;
struct stub_priv *priv;
struct usbip_device *ud = &sdev->ud;
- struct usb_device *udev = interface_to_usbdev(sdev->interface);
+ struct usb_device *udev = sdev->udev;
int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction);
diff --git a/drivers/staging/usbip/vhci.h b/drivers/staging/usbip/vhci.h
index 41a1fe5138f4..afc3b1a71881 100644
--- a/drivers/staging/usbip/vhci.h
+++ b/drivers/staging/usbip/vhci.h
@@ -100,9 +100,6 @@ struct vhci_hcd {
* 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;
};
@@ -119,6 +116,9 @@ void rh_port_disconnect(int rhport);
void vhci_rx_loop(struct usbip_task *ut);
void vhci_tx_loop(struct usbip_task *ut);
+struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
+ __u32 seqnum);
+
#define hardware (&the_controller->pdev.dev)
static inline struct vhci_device *port_to_vdev(__u32 port)
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index 08bd26a245d5..a35fe61268de 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -138,8 +138,6 @@ void rh_port_connect(int rhport, enum usb_device_speed speed)
* the_controller->vdev[rhport].ud.status = VDEV_CONNECT;
* spin_unlock(&the_controller->vdev[rhport].ud.lock); */
- the_controller->pending_port = rhport;
-
spin_unlock_irqrestore(&the_controller->lock, flags);
usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
@@ -559,6 +557,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
struct device *dev = &urb->dev->dev;
int ret = 0;
unsigned long flags;
+ struct vhci_device *vdev;
usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
hcd, urb, mem_flags);
@@ -574,6 +573,18 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
return urb->status;
}
+ vdev = port_to_vdev(urb->dev->portnum-1);
+
+ /* refuse enqueue for dead connection */
+ spin_lock(&vdev->ud.lock);
+ if (vdev->ud.status == VDEV_ST_NULL || vdev->ud.status == VDEV_ST_ERROR) {
+ usbip_uerr("enqueue for inactive port %d\n", vdev->rhport);
+ spin_unlock(&vdev->ud.lock);
+ spin_unlock_irqrestore(&the_controller->lock, flags);
+ return -ENODEV;
+ }
+ spin_unlock(&vdev->ud.lock);
+
ret = usb_hcd_link_urb_to_ep(hcd, urb);
if (ret)
goto no_need_unlink;
@@ -592,8 +603,6 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
__u8 type = usb_pipetype(urb->pipe);
struct usb_ctrlrequest *ctrlreq =
(struct usb_ctrlrequest *) urb->setup_packet;
- struct vhci_device *vdev =
- port_to_vdev(the_controller->pending_port);
if (type != PIPE_CONTROL || !ctrlreq) {
dev_err(dev, "invalid request to devnum 0\n");
@@ -607,7 +616,9 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
dev_info(dev, "SetAddress Request (%d) to port %d\n",
ctrlreq->wValue, vdev->rhport);
- vdev->udev = urb->dev;
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = usb_get_dev(urb->dev);
spin_lock(&vdev->ud.lock);
vdev->ud.status = VDEV_ST_USED;
@@ -627,8 +638,9 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
"Get_Descriptor to device 0 "
"(get max pipe size)\n");
- /* FIXME: reference count? (usb_get_dev()) */
- vdev->udev = urb->dev;
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = usb_get_dev(urb->dev);
goto out;
default:
@@ -805,7 +817,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
return 0;
}
-
static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
{
struct vhci_unlink *unlink, *tmp;
@@ -813,11 +824,34 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
+ usbip_uinfo("unlink cleanup tx %lu\n", unlink->unlink_seqnum);
list_del(&unlink->list);
kfree(unlink);
}
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
+ struct urb *urb;
+
+ /* give back URB of unanswered unlink request */
+ usbip_uinfo("unlink cleanup rx %lu\n", unlink->unlink_seqnum);
+
+ urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
+ if (!urb) {
+ usbip_uinfo("the urb (seqnum %lu) was already given back\n",
+ unlink->unlink_seqnum);
+ list_del(&unlink->list);
+ kfree(unlink);
+ continue;
+ }
+
+ urb->status = -ENODEV;
+
+ 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);
+
list_del(&unlink->list);
kfree(unlink);
}
@@ -887,6 +921,10 @@ static void vhci_device_reset(struct usbip_device *ud)
vdev->speed = 0;
vdev->devid = 0;
+ if (vdev->udev)
+ usb_put_dev(vdev->udev);
+ vdev->udev = NULL;
+
ud->tcp_socket = NULL;
ud->status = VDEV_ST_NULL;
diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c
index 8147d7202b2d..bf6991470941 100644
--- a/drivers/staging/usbip/vhci_rx.c
+++ b/drivers/staging/usbip/vhci_rx.c
@@ -23,16 +23,14 @@
#include "vhci.h"
-/* get URB from transmitted urb queue */
-static struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
+/* 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;
- spin_lock(&vdev->priv_lock);
-
list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
if (priv->seqnum == seqnum) {
urb = priv->urb;
@@ -63,8 +61,6 @@ static struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
}
}
- spin_unlock(&vdev->priv_lock);
-
return urb;
}
@@ -74,9 +70,11 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
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) {
usbip_uerr("cannot find a urb of seqnum %u\n",
@@ -161,7 +159,12 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
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
@@ -190,6 +193,19 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
return;
}
+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)
{
@@ -202,11 +218,29 @@ static void vhci_rx_pdu(struct usbip_device *ud)
memset(&pdu, 0, sizeof(pdu));
-
/* 1. receive a pdu header */
ret = usbip_xmit(0, ud->tcp_socket, (char *) &pdu, sizeof(pdu), 0);
+ if (ret < 0) {
+ if (ret == -ECONNRESET)
+ usbip_uinfo("connection reset by peer\n");
+ else if (ret == -EAGAIN) {
+ /* ignore if connection was idle */
+ if (vhci_priv_tx_empty(vdev))
+ return;
+ usbip_uinfo("connection timed out with pending urbs\n");
+ } else if (ret != -ERESTARTSYS)
+ usbip_uinfo("xmit failed %d\n", ret);
+
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return;
+ }
+ if (ret == 0) {
+ usbip_uinfo("connection closed");
+ usbip_event_add(ud, VDEV_EVENT_DOWN);
+ return;
+ }
if (ret != sizeof(pdu)) {
- usbip_uerr("receiving pdu failed! size is %d, should be %d\n",
+ usbip_uerr("received pdu size is %d, should be %d\n",
ret, (unsigned int)sizeof(pdu));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
return;