]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/usbip/vhci_rx.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / staging / usbip / vhci_rx.c
index 8147d7202b2dc092e8986891baaf463e6e3f1bf3..bf69914709410cf054825933a638b7dd7060180a 100644 (file)
 #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;