Hi,

        I've been working late on the irda-usb driver. Lot's of errors
and timeout from the USB layer were not handled properly. As I have a
platform with is weird, those errors were showing all over the place.
        On my PC were it was working, it is still working properly, so
I guess I didn't broke anything. On my other platform, it now crash
somewhere else (argh !!!).

        Enjoy...

        Jean
diff -u -p linux/drivers/net/irda/irda-usb.j1.h linux/drivers/net/irda/irda-usb.h
--- linux/drivers/net/irda/irda-usb.j1.h        Mon Feb 26 17:36:33 2001
+++ linux/drivers/net/irda/irda-usb.h   Thu Mar  1 17:07:44 2001
@@ -32,9 +32,9 @@
 
 #define RX_COPY_THRESHOLD 200
 #define IRDA_USB_MAX_MTU 2050 
-#define IRDA_USB_SPEED_MTU 64          /* Weird, but work like this */
+#define IRDA_USB_SPEED_MTU 128         /* Weird, but work like this */
 
-/* Maximum number of URB on the Rx and Tx path
+/* Maximum number of active URB on the Rx path
  * This is the amount of buffers the we keep between the USB harware and the
  * IrDA stack.
  * Note : the network layer does also queue the packets between us and the
@@ -47,7 +47,21 @@
  * On the other hand, increasing the number of URB will have penalities
  * in term of latency and will interact with the link management in IrLAP...
  * Jean II */
-#define IU_MAX_URB     1       /* Don't touch !!! */
+#define IU_MAX_ACTIVE_URB      1       /* Don't touch !!! */
+/* When a Rx URB is passed back to us, we can't reuse it immediately,
+ * because it may still be referenced by the USB layer. Therefore we
+ * need to keep one extra URB in the Rx path.
+ * Jean II */
+#define IU_MAX_URB     (IU_MAX_ACTIVE_URB + 1)
+
+/* Various ugly stuff to try to workaround problems */
+/* The USB layer should send empty frames at the end of packet. As it
+ * doesn't do it, we need to do it ourselves... */
+#define IU_BUG_KICK_TX
+/* Send empty frame before setting the speed, same reason */
+//#define IU_BUG_KICK_SPEED    -> Make things worse
+/* Send speed command in case of timeout, same reason */
+#define IU_BUG_KICK_TIMEOUT
 
 /* Inbound header */
 #define MEDIA_BUSY    0x80
@@ -103,8 +117,10 @@ struct irda_usb_cb {
        wait_queue_head_t wait_q;       /* for timeouts */
 
        struct urb rx_urb[IU_MAX_URB];  /* URBs used to receive data frames */
+       struct urb *idle_rx_urb;        /* Pointer to idle URB in Rx path */
        struct urb tx_urb;              /* URB used to send data frames */
        struct urb speed_urb;           /* URB used to send speed commands */
+       struct urb empty_urb;           /* URB used to send empty commands */
        
        struct net_device *netdev;      /* Yes! we are some kind of netdev. */
        struct net_device_stats stats;
@@ -113,6 +129,7 @@ struct irda_usb_cb {
        iobuff_t tx_buff;               /* Transmit buffer */
        iobuff_t rx_buff;               /* Receive buffer */
        iobuff_t speed_buff;            /* Buffer for speed changes */
+       iobuff_t empty_buff;            /* Buffer for empty commands */
 
        struct timeval stamp;
        struct timeval now;
diff -u -p linux/drivers/net/irda/irda-usb.j1.c linux/drivers/net/irda/irda-usb.c
--- linux/drivers/net/irda/irda-usb.j1.c        Mon Feb 26 17:33:59 2001
+++ linux/drivers/net/irda/irda-usb.c   Thu Mar  1 17:09:40 2001
@@ -424,6 +424,14 @@ static int irda_usb_open(struct irda_usb
                return -1;
        memset(self->speed_buff.head, 0, self->speed_buff.truesize);
        self->speed_buff.data = self->speed_buff.head;
+       /* Same for empty buff */
+       self->empty_buff.truesize = IRDA_USB_SPEED_MTU;
+       self->empty_buff.head = (__u8 *) kmalloc(self->empty_buff.truesize, 
+                                                GFP_KERNEL);
+       if (self->empty_buff.head == NULL) 
+               return -1;
+       memset(self->empty_buff.head, 0, self->empty_buff.truesize);
+       self->empty_buff.data = self->empty_buff.head;
 
        /* Create a network device for us */
        if (!(netdev = dev_alloc("irda%d", &err))) {
@@ -476,6 +484,10 @@ static int irda_usb_close(struct irda_us
                kfree(self->speed_buff.head);
                self->speed_buff.head = NULL;
        }
+       if (self->empty_buff.head) {
+               kfree(self->empty_buff.head);
+               self->empty_buff.head = NULL;
+       }
        
        return 0;
 }
@@ -490,6 +502,41 @@ static int irda_usb_send_cmd(void)
 #endif
 
 /*
+ * Send an empty URB to the dongle
+ * The goal there is to try to resynchronise with the dongle. An empty
+ * frame signify the end of a Tx frame. Jean II
+ */
+static inline void irda_usb_send_empty(struct irda_usb_cb *self)
+{
+       purb_t purb;
+       int ret;
+
+       IRDA_DEBUG(0, __FUNCTION__ "()\n");
+
+       /* Grab the empty URB */
+       purb = &self->empty_urb;
+       if (purb->status != USB_ST_NOERROR) {
+               WARNING(__FUNCTION__ "(), Empty URB still in use!\n");
+               return;
+       }
+
+       /* Submit the Empty URB */
+        FILL_BULK_URB(purb, self->usbdev,
+                     usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
+                      self->empty_buff.head, IRDA_USB_SPEED_MTU,
+                      write_bulk_callback, self);
+       purb->transfer_buffer_length = 0;
+       //purb->transfer_flags |= USB_ASYNC_UNLINK;
+       //purb->transfer_flags |= USB_QUEUE_BULK;
+       purb->transfer_flags = USB_QUEUE_BULK;
+       purb->timeout = MSECS_TO_JIFFIES(100);
+
+       if ((ret = usb_submit_urb(purb))) {
+               IRDA_DEBUG(0, __FUNCTION__ "(), failed Empty URB\n");
+       }
+}
+
+/*
  * Function irda_usb_build_header(self, skb, header)
  *
  *   Builds USB-IrDA outbound header
@@ -607,6 +654,16 @@ static void irda_usb_change_speed_xbofs(
        IRDA_DEBUG(0, __FUNCTION__ "(), speed=%d, xbofs=%d\n",
                   self->new_speed, self->new_xbofs);
 
+#ifdef IU_BUG_KICK_SPEED
+       /* Before sending the speed command, make sure that the dongle
+        * is ready by sending an empty frame - Jean II */
+       IRDA_DEBUG(3, __FUNCTION__ "(), Kick Speed...\n");
+       spin_lock_irqsave(&self->lock, flags);
+       irda_usb_send_empty(self);
+       spin_unlock_irqrestore(&self->lock, flags);
+#endif /* IU_BUG_KICK_SPEED */
+
+       /* Grab the speed URB */
        purb = &self->speed_urb;
        if (purb->status != USB_ST_NOERROR) {
                WARNING(__FUNCTION__ "(), URB still in use!\n");
@@ -624,11 +681,12 @@ static void irda_usb_change_speed_xbofs(
        /* Submit the 0 length IrDA frame to trigger new speed settings */
         FILL_BULK_URB(purb, self->usbdev,
                      usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
-                      frame, IRDA_USB_MAX_MTU,
+                      frame, IRDA_USB_SPEED_MTU,
                       write_bulk_callback, self);
        purb->transfer_buffer_length = USB_IRDA_HEADER;
        //purb->transfer_flags |= USB_ASYNC_UNLINK;
-       purb->transfer_flags |= USB_QUEUE_BULK;
+       //purb->transfer_flags |= USB_QUEUE_BULK;
+       purb->transfer_flags = USB_QUEUE_BULK;
        purb->timeout = MSECS_TO_JIFFIES(100);
 
        if ((ret = usb_submit_urb(purb))) {
@@ -723,7 +781,8 @@ static int irda_usb_hard_xmit(struct sk_
                       self->tx_buff.head, IRDA_USB_MAX_MTU,
                       write_bulk_callback, self);
        purb->transfer_buffer_length = self->tx_buff.len;
-       purb->transfer_flags |= USB_QUEUE_BULK;
+       //purb->transfer_flags |= USB_QUEUE_BULK;
+       purb->transfer_flags = USB_QUEUE_BULK;
        purb->timeout = MSECS_TO_JIFFIES(100);
 
        if ((res = usb_submit_urb(purb))) {
@@ -731,25 +790,20 @@ static int irda_usb_hard_xmit(struct sk_
                self->stats.tx_errors++;
                netif_start_queue(netdev);
        } else {
+#ifdef IU_BUG_KICK_TX
                /* Kick Tx?
                 * If the packet is a multiple of 64, the USB layer
                 * should send an empty frame (a short packet) to signal
                 * the end of frame (that's part of the USB spec).
                 * In theory, we would need to do that only if USB_DISABLE_SPD
                 * is not enabled, and with the current setting the USB layer
-                * should do it for us.
-                * So why do we need this workaround ? Mystery... */
+                * should do it for us. However, the USB layer doesn't handle
+                * USB_DISABLE_SPD properly, so we need this workaround... */
                if ((self->tx_buff.len % 64) == 0) {
-                       /* Borrow speed urb to send empty frame */
-                       purb = &self->speed_urb;
-                       FILL_BULK_URB(purb, self->usbdev,
-                                     usb_sndbulkpipe(self->usbdev, self->bulk_out_ep),
-                                     self->tx_buff.head, IRDA_USB_MAX_MTU,
-                                     write_bulk_callback, self);
-                       purb->transfer_buffer_length = 0;
-                       purb->transfer_flags |= USB_QUEUE_BULK;
-                       res = usb_submit_urb(purb);
+                       IRDA_DEBUG(3, __FUNCTION__ "(), Kick Tx...\n");
+                       irda_usb_send_empty(self);
                }
+#endif /* IU_BUG_KICK_TX */
                self->stats.tx_packets++;
                 self->stats.tx_bytes += skb->len;
                
@@ -776,6 +830,19 @@ static void write_bulk_callback(purb_t p
                return;
        }
 
+       /* Check for timeout and other USB nasties */
+       if(purb->status != USB_ST_NOERROR) {
+               /* I get a lot of -ECONNABORTED = -103 here - Jean II */
+               WARNING(__FUNCTION__ "(), URB complete status %d, transfer_flags 
+0x%04X\n", purb->status, purb->transfer_flags);
+
+               /* Don't do anything here, that might confuse the USB layer.
+                * Instead, we will wait for irda_usb_net_timeout(), the
+                * network layer watchdog, to fix the situation.
+                * Jean II */
+               /* A reset of the dongle might be welcomed here - Jean II */
+               return;
+       }
+
        /* urb is now available */
        purb->status = USB_ST_NOERROR;
 
@@ -785,6 +852,12 @@ static void write_bulk_callback(purb_t p
                return;
        }
 
+       /* If it was the empty URB, don't do anything... */
+       if(purb == &self->empty_urb) {
+               IRDA_DEBUG(3, __FUNCTION__ "(), Empty URB completed...\n");
+               return;
+       }
+
        /* If we need to change the speed or xbofs, do it now */
        if ((self->new_speed != -1) || (self->new_xbofs != -1)) {
                IRDA_DEBUG(0, __FUNCTION__ "(), Changing speed now...\n");
@@ -795,6 +868,9 @@ static void write_bulk_callback(purb_t p
        }
 }
 
+/*
+ * Submit a Rx URB to the USB layer to handle reception of a frame
+ */
 static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, purb_t 
purb)
 {
        struct irda_skb_cb *cb;
@@ -834,7 +910,8 @@ static void irda_usb_submit(struct irda_
                      usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep), 
                      skb->data, skb->truesize,
                       irda_usb_receive, skb);
-       purb->transfer_flags |= USB_QUEUE_BULK;
+       //purb->transfer_flags |= USB_QUEUE_BULK;
+       purb->transfer_flags = USB_QUEUE_BULK;
        purb->status = USB_ST_NOERROR;
        
        ret = usb_submit_urb(purb);
@@ -843,6 +920,42 @@ static void irda_usb_submit(struct irda_
                 * Basically, the Rx path will stop... */
                IRDA_DEBUG(0, __FUNCTION__ "(), Failed to submit Rx URB %d\n", ret);
        }
+
+       /* Important note :
+        * The function process_urb() contains the following code :
+        * >    urb->complete ((struct urb *) urb);
+        * >    // Re-submit the URB if ring-linked
+        * >    if (is_ring && (urb->status != -ENOENT) && !contains_killed) {
+        * >            urb->dev=usb_dev;
+        * >            uhci_submit_urb (urb);
+        * >    }
+        * The way I see it is that if we submit more than one Rx URB at a
+        * time, the Rx URB are automatically re-submitted after the
+        * completion handler is called.
+        * What is weird is that most driver also re-submit the Rx URB in
+        * the completion handler. No comments...
+        *
+        * My take is that it's a questionable feature, and quite difficult
+        * to control and to make work effectively.
+        * The outcome (re-submited or not) depend on various complex
+        * test (is_ring and contains_killed), and the completion handler
+        * don't have this information, so basically the driver has no way
+        * to know if URB are resubmitted or not. Yuck !
+        * If everything is perfect, it's cool, but the problem is when
+        * an URB is killed (timeout, call to unlink_urb(), ...), things get
+        * messy...
+        * The other problem is that this scheme deal only with the URB
+        * and ignore everything about the associated buffer. So, it would
+        * resubmit URB even if the buffer is still in use or non-existent.
+        * On the other hand, submitting ourself in the completion callback
+        * is quite trivial and work well (see above).
+        *
+        * Moreover, this scheme will interfer with the idle_rx_urb stuff
+        * down there if we submit more than one URB (so, don't do that).
+        * I wish it was possible to disable this feature...
+        *
+        * Jean II
+        */
 }
 
 /*
@@ -881,8 +994,14 @@ static void irda_usb_receive(purb_t purb
                        self->stats.rx_errors++;
                        self->stats.rx_crc_errors++;    
                        break;
+               case -ECONNRESET:               /* -104 */
+                       WARNING(__FUNCTION__ "(), Connection Reset !!!\n");
+                       /* uhci_cleanup_unlink() is going to kill the Rx
+                        * URB just after we return. No problem, at this
+                        * point the URB will be idle ;-) - Jean II */
+                       break;
                default:
-                       WARNING(__FUNCTION__ "(), RX status %d\n", purb->status);
+                       WARNING(__FUNCTION__ "(), RX status %d,transfer_flags 0x%04X 
+\n", purb->status, purb->transfer_flags);
                        break;
                }
                goto done;
@@ -917,6 +1036,7 @@ static void irda_usb_receive(purb_t purb
                
                /* Copy packet, so we can recycle the original */
                memcpy(skb_put(new, skb->len), skb->data, skb->len);
+               /* We will cleanup the skb in irda_usb_submit() */
        } else {
                /* Deliver the original skb */
                new = skb;
@@ -931,9 +1051,25 @@ static void irda_usb_receive(purb_t purb
         new->mac.raw  = new->data;
         new->protocol = htons(ETH_P_IRDA);
         netif_rx(new);
+
 done:
-       /* Recycle Rx URB (and possible the skb as well) */
-       irda_usb_submit(self, skb, purb);
+       /* Note : at this point, the URB we've just received (purb)
+        * is still referenced by the USB layer. For example, if we
+        * have received a -ECONNRESET, uhci_cleanup_unlink() will
+        * continue to process it (in fact, cleaning it up).
+        * If we were to submit this URB, disaster would ensue.
+        * Therefore, we submit our idle URB, and put this URB in our
+        * idle slot....
+        * Jean II */
+       /* Note : with this scheme, we could submit the idle URB before
+        * processing the Rx URB. Another time... Jean II */
+
+       /* Submit the idle URB to replace the URB we've just received */
+       irda_usb_submit(self, (struct sk_buff *) self->idle_rx_urb->context,
+                       self->idle_rx_urb);
+       /* Recycle Rx URB : Now, the idle URB is the present one */
+       self->idle_rx_urb = purb;
+       self->idle_rx_urb->context = skb;
 }
 
 static int irda_usb_net_init(struct net_device *dev)
@@ -998,8 +1134,11 @@ static int irda_usb_net_open(struct net_
 
        /* Now that we can pass data to IrLAP, allow the USB layer
         * to send us some data... */
-       for (i = 0; i < IU_MAX_URB; i++)
+       for (i = 0; i < IU_MAX_ACTIVE_URB; i++)
                irda_usb_submit(self, NULL, &(self->rx_urb[i]));
+       /* Note : we submit all the Rx URB except for one - Jean II */
+       self->idle_rx_urb = &(self->rx_urb[IU_MAX_ACTIVE_URB]);
+       self->idle_rx_urb->context = NULL;
 
        /* Ready to play !!! */
        MOD_INC_USE_COUNT;
@@ -1068,39 +1207,92 @@ static void irda_usb_net_timeout(struct 
                return;
        }
 
-       WARNING("%s: Tx timed out, urb->status=%d\n", netdev->name, purb->status);
-       self->stats.tx_errors++;
+       /* Check empty URB */
+       purb = &(self->empty_urb);
+       if (purb->status != USB_ST_NOERROR) {
+               WARNING("%s: Empty change timed out, urb->status=%d, 
+urb->transfer_flags=0x%04X\n", netdev->name, purb->status, purb->transfer_flags);
 
-       /* Check Tx URB */
-       purb = &(self->tx_urb);
-       switch (purb->status) {
-               case -ECONNABORTED:     /* Can't find proper USB_ST_* code */
+               switch (purb->status) {
+               case -ECONNABORTED:             /* -103 */
+               case -ECONNRESET:               /* -104 */
+               case -ENOENT:                   /* -2 */
                        purb->status = USB_ST_NOERROR;
-                       netif_wake_queue(self->netdev);
                        done = 1;
                        break;
                case USB_ST_URB_PENDING:        /* -EINPROGRESS == -115 */
                        usb_unlink_urb(purb);
+                       /* Note : above will *NOT* call netif_wake_queue()
+                        * in completion handler - Jean II */
                        done = 1;
                        break;
                default:
+                       /* ??? */
                        break;
+               }
        }
+
        /* Check speed URB */
        purb = &(self->speed_urb);
-       switch (purb->status) {
-               case -ECONNABORTED:     /* Can't find proper USB_ST_* code */
+       if (purb->status != USB_ST_NOERROR) {
+               WARNING("%s: Speed change timed out, urb->status=%d, 
+urb->transfer_flags=0x%04X\n", netdev->name, purb->status, purb->transfer_flags);
+
+               switch (purb->status) {
+               case -ECONNABORTED:             /* -103 */
+               case -ECONNRESET:               /* -104 */
+               case -ENOENT:                   /* -2 */
                        purb->status = USB_ST_NOERROR;
                        netif_wake_queue(self->netdev);
                        done = 1;
                        break;
-               case USB_ST_URB_PENDING:        /* -EINPROGRESS */
+               case USB_ST_URB_PENDING:        /* -EINPROGRESS == -115 */
                        usb_unlink_urb(purb);
+                       /* Note : above will call netif_wake_queue()
+                        * in completion handler - Jean II */
                        done = 1;
                        break;
                default:
+                       /* ??? */
                        break;
+               }
        }
+
+       /* Check Tx URB */
+       purb = &(self->tx_urb);
+       if (purb->status != USB_ST_NOERROR) {
+               WARNING("%s: Tx timed out, urb->status=%d, 
+urb->transfer_flags=0x%04X\n", netdev->name, purb->status, purb->transfer_flags);
+
+               /* Increase error count */
+               self->stats.tx_errors++;
+
+#ifdef IU_BUG_KICK_TIMEOUT
+               /* Can't be a bad idea to reset the speed ;-) - Jean II */
+               if(self->new_speed == -1)
+                       self->new_speed = self->speed;
+               if(self->new_xbofs == -1)
+                       self->new_xbofs = self->xbofs;
+               irda_usb_change_speed_xbofs(self);
+#endif /* IU_BUG_KICK_TIMEOUT */
+
+               switch (purb->status) {
+               case -ECONNABORTED:             /* -103 */
+               case -ECONNRESET:               /* -104 */
+               case -ENOENT:                   /* -2 */
+                       purb->status = USB_ST_NOERROR;
+                       netif_wake_queue(self->netdev);
+                       done = 1;
+                       break;
+               case USB_ST_URB_PENDING:        /* -EINPROGRESS == -115 */
+                       usb_unlink_urb(purb);
+                       /* Note : above will call netif_wake_queue()
+                        * in completion handler - Jean II */
+                       done = 1;
+                       break;
+               default:
+                       /* ??? */
+                       break;
+               }
+       }
+
        /* Maybe we need a reset */
        /* Note : Some drivers seem to use a usb_set_interface() when they
         * need to reset the hardware. Hum...

Reply via email to