Hi folks, 

This patch is a cleaner version of my hacked pegasus driver. It works
almost great on my (broken ?) OHCI machine, doesn't crash, and spits out
solid 200KByte/second on my system (That poor P133 CPU seems to be the
bootleneck, but it seems OK, since D-Link advertised their toy for P166
minimum). At least now, NFS, FTP, X, SSH, APT *and* LPRNG are usable
(LPRNG is really really sensible to the smallest timeouts and lost
packets, which made the stock driver or my early "failover-with-thread"
approach unusable).

I'd really be happy to hear about people testing this one, on both host
interfaces.

Issues still to check :
        * does it work on UHCI as great as unpatched 0.3.13 did ? 
        * on OHCI, requires 2.4.0-test1-ac7 *and* my little OHCI fix I've
sent here yesterday. Also, there are some conditions now (UE) where the
OHCI controller will disable itself ; basically what's needed now is to
recover from what the controller says is an unrecoverable error. OHCI
wizards, any idea ?
        * what happens when there are still timeouts (probably on a
CPU-challenged machine like mine) and the host controller honours
urb->timeout (which should keep the NETDEV watchdog silent) ? I can't test
this code path on current OHCI, and my UHCI machine is too critical to
allow too much of tinkering with it.

        -- Cyrille

------------------------------------------------------------------------------
Grumpf.

--- drivers/usb/pegasus.orig.c  Thu Jun  1 18:02:59 2000
+++ drivers/usb/pegasus.c       Fri Jun  2 08:01:12 2000
@@ -16,15 +16,18 @@
 #include <linux/usb.h>
 
 
-static const char *version = __FILE__ ": v0.3.13 2000/05/25 (C) 1999-2000 Petko 
Manolov ([EMAIL PROTECTED])\n";
+static const char *version = __FILE__ ": v0.3.13h29 2000/05/25 (C) 1999-2000 Petko 
+Manolov ([EMAIL PROTECTED])\n";
 
 
 #define        PEGASUS_MTU             1500
 #define        PEGASUS_MAX_MTU         1536
 #define        SROM_WRITE              0x01
 #define        SROM_READ               0x02
-#define        PEGASUS_TX_TIMEOUT      (HZ*5)
+#define PEGASUS_TX_TIMEOUT      (HZ/15)
+#define        PEGASUS_NET_TX_TIMEOUT  (11*PEGASUS_TX_TIMEOUT)/10
 #define        PEGASUS_RESET           1
+#define PEGASUS_IN_RESET        2
+#define PEGASUS_CLOSING         4
 #define        ALIGN(x)                x __attribute__((aligned(L1_CACHE_BYTES)))
 
 
@@ -34,7 +37,15 @@
        struct net_device_stats stats;
        int                     flags;
        spinlock_t              pegasus_lock;
-       struct urb              rx_urb, tx_urb, intr_urb;
+       struct urb              rx_urb, tx_urb, intr_urb, ctrl_urb;
+       int                     can_resubmit;
+       unsigned char           d01;
+       
+       atomic_t                resetpending;
+
+       devrequest              ALIGN(ctrl_request);
+       unsigned char           ALIGN(ctrl_req_data);
+
        unsigned char           ALIGN(rx_buff[PEGASUS_MAX_MTU]); 
        unsigned char           ALIGN(tx_buff[PEGASUS_MAX_MTU]); 
        unsigned char           ALIGN(intr_buff[8]);
@@ -75,7 +86,6 @@
        {NULL, 0, 0, NULL}
 };
 
-
 #define pegasus_get_registers(dev, indx, size, data)\
        usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, 
HZ);
 #define pegasus_set_registers(dev, indx, size, data)\
@@ -176,7 +186,8 @@
 }
 
 
-static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
+static int pegasus_start_net(struct net_device *dev, struct usb_device *usb,
+                            struct pegasus *pegasus)
 {
        __u16 partmedia, temp;
        __u8 node_id[6];
@@ -208,6 +219,7 @@
        data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
        data[2] = (loopback & 1) ? 0x09 : 0x01;
 
+       pegasus->d01 = data[1]|0x08; /* reset MAC bit */
        pegasus_set_registers(usb, 0, 3, data);
 
        return 0;
@@ -268,12 +280,62 @@
 
 static void pegasus_irq(urb_t *urb)
 {
+       struct pegasus *pegasus = urb->context;
        __u8    *d = urb->transfer_buffer;
-       
+
        if ( d[0] )
                dbg("txst0=0x%2x", d[0]);
 }
 
+static void pegasus_async_set_register(struct pegasus *pegasus, __u16 index,
+                                      __u16 value)
+{
+       int res;
+
+       /* we already hold pegasus->pegasus_lock here */
+       pegasus->ctrl_request.requesttype = 0x40;
+       pegasus->ctrl_request.request = 0xf1;
+       pegasus->ctrl_request.value = cpu_to_le16(value);
+       pegasus->ctrl_request.index = cpu_to_le16(index);
+       pegasus->ctrl_request.length = cpu_to_le16(sizeof(char));
+       pegasus->ctrl_req_data = (value & 0xFF);
+
+       if ((res = usb_submit_urb(&pegasus->ctrl_urb))) {
+               warn("%s control urb failed %d",pegasus->net->name,res);
+       }
+}
+
+static void pegasus_reset_complete(struct urb *urb) {
+       struct pegasus *pegasus = urb->context;
+
+       spin_lock(&pegasus->pegasus_lock);
+       
+       if (urb->status) 
+               warn("%s: reset status %d",pegasus->net->name,urb->status);
+       pegasus->flags &= ~(PEGASUS_RESET | PEGASUS_IN_RESET);
+
+       netif_start_queue(pegasus->net);
+       netif_schedule(pegasus->net);
+       spin_unlock(&pegasus->pegasus_lock);
+}
+
+static void pegasus_do_reset(struct pegasus *pegasus) {
+       int res;
+
+       spin_lock(&pegasus->pegasus_lock);
+
+       if (pegasus->ctrl_urb.status == -EINPROGRESS) {
+               warn("control URB already in progress.");
+       }
+       if (pegasus->flags & PEGASUS_IN_RESET) {
+               dbg("reset already in progress. Not resetting twice.");
+       } else {
+               pegasus->flags |= PEGASUS_IN_RESET;
+               pegasus->ctrl_urb.complete = pegasus_reset_complete;
+               pegasus_async_set_register(pegasus,0x01,(__u8)pegasus->d01);
+       }
+       spin_unlock(&pegasus->pegasus_lock);
+}
 
 static void pegasus_write_bulk(struct urb *urb)
 {
@@ -281,10 +343,17 @@
 
        spin_lock(&pegasus->pegasus_lock);
 
-       if (urb->status)
-               info("%s: TX status %d", pegasus->net->name, urb->status);
-       netif_wake_queue(pegasus->net);
+       switch (urb->status) {
+       case 0:           /* 0/0 says the little box */
+       case -ECONNRESET: /* async unlink happened */
+               break;
+       default:
+               warn("%s: TX status %d", pegasus->net->name, urb->status);
+       }
 
+       if (!(pegasus->flags & PEGASUS_CLOSING)) {
+               netif_wake_queue(pegasus->net);
+       }
        spin_unlock(&pegasus->pegasus_lock);
 }
 
@@ -292,13 +361,14 @@
 {
        struct pegasus *pegasus = net->priv;
 
-       warn("%s: Tx timed out. Reseting...", net->name);
+       dbg("%s: Tx timed out. Reseting...", net->name);
        usb_unlink_urb(&pegasus->tx_urb);
        pegasus->stats.tx_errors++;
        net->trans_start = jiffies;
        pegasus->flags |= PEGASUS_RESET;
-
-       netif_wake_queue(net);
+       
+       pegasus_do_reset(pegasus);
+       /* netif_wake_queue when reset is complete */
 }
 
 
@@ -309,25 +379,45 @@
        int res;
 
        spin_lock(&pegasus->pegasus_lock);
-
        netif_stop_queue(net);
 
+       if (pegasus->tx_urb.status == -EINPROGRESS) {
+               /* attempting to send a packet while another is in flight. 
+                  Dropping packet on the floor. This might be the 
+                  symptom of a race condition. */
+               pegasus->stats.tx_errors++;
+               netif_start_queue(net);
+
+               goto xmit_goon;
+       }
+       
        ((__u16 *)pegasus->tx_buff)[0] = skb->len;
        memcpy(pegasus->tx_buff+2, skb->data, skb->len);
        (&pegasus->tx_urb)->transfer_buffer_length = count;
 
        if ((res = usb_submit_urb(&pegasus->tx_urb))) {
-               warn("failed tx_urb %d", res);
+               warn("failed tx_urb %d status %d", res,pegasus->tx_urb.status);
+               if ((res == -EINVAL) || (res == -ENOMEM)) {
+                       switch (pegasus->tx_urb.status) {
+                       case -EINPROGRESS:
+                       case -ENOENT:
+                               res = usb_unlink_urb(&pegasus->tx_urb);
+                               if (res) {
+                                       if (res != -EINPROGRESS) 
+                                               warn("failed tx_urb unlink %d",
+                                                    res);
+                               }
+                       }
+               }
                pegasus->stats.tx_errors++;
-               netif_start_queue(net);
        } else {
                pegasus->stats.tx_packets++;
                pegasus->stats.tx_bytes += skb->len;
                net->trans_start = jiffies;
        }
 
-       dev_kfree_skb(skb);
-
+ xmit_goon:
+       dev_kfree_skb_any(skb);
        spin_unlock(&pegasus->pegasus_lock);
 
        return 0;
@@ -345,7 +435,7 @@
        struct pegasus *pegasus = (struct pegasus *)net->priv;
        int res;
 
-       if ((res = pegasus_start_net(net, pegasus->usb))) {
+       if ((res = pegasus_start_net(net, pegasus->usb, pegasus))) {
                err("can't start_net() - %d", res);
                return -EIO;
        }
@@ -368,7 +458,11 @@
 {
        struct pegasus  *pegasus = net->priv;
 
+       spin_lock(&pegasus->pegasus_lock);
+       pegasus->flags |= PEGASUS_CLOSING;
+       pegasus->can_resubmit = 0;
        netif_stop_queue(net);
+       spin_unlock(&pegasus->pegasus_lock);
 
        if ( pegasus->rx_urb.status == -EINPROGRESS )
                usb_unlink_urb(&pegasus->rx_urb);
@@ -376,6 +470,8 @@
                usb_unlink_urb(&pegasus->tx_urb);
        if ( pegasus->intr_urb.status == -EINPROGRESS )
                usb_unlink_urb(&pegasus->intr_urb);
+       if ( pegasus->ctrl_urb.status == -EINPROGRESS )
+               usb_unlink_urb(&pegasus->ctrl_urb);
 
        MOD_DEC_USE_COUNT;
 
@@ -472,7 +568,7 @@
        net->priv = pegasus;
        net->open = pegasus_open;
        net->stop = pegasus_close;
-       net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+       net->watchdog_timeo = PEGASUS_NET_TX_TIMEOUT;
        net->tx_timeout = pegasus_tx_timeout;
        net->do_ioctl = pegasus_ioctl;
        net->hard_start_xmit = pegasus_start_xmit;
@@ -483,16 +579,28 @@
        pegasus->usb = dev;
        pegasus->net = net;
        pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
-
+       
+       FILL_CONTROL_URB(&pegasus->ctrl_urb,dev,usb_sndctrlpipe(dev,0),
+                        (char *)&pegasus->ctrl_request,
+                        &pegasus->ctrl_req_data,
+                        sizeof(pegasus->ctrl_req_data),pegasus_reset_complete,
+                        pegasus);
+       pegasus->ctrl_urb.transfer_flags |= USB_ASYNC_UNLINK;
        FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
                        pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, 
                        pegasus);
        FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
                        pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk,
                        pegasus);
+       pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK;
        FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3),
                        pegasus->intr_buff, 8, pegasus_irq, pegasus, 500);
 
+       pegasus->tx_urb.timeout = PEGASUS_TX_TIMEOUT;
+
+       pegasus->flags &= ~PEGASUS_CLOSING;
+
+       pegasus->can_resubmit = 1;
 
        printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name);
 
@@ -514,10 +622,18 @@
 
        unregister_netdev(pegasus->net);
 
+        pegasus->flags |= PEGASUS_CLOSING;
+
+       if ( pegasus->ctrl_urb.status == -EINPROGRESS ) {
+               pegasus->ctrl_urb.transfer_flags &= ~USB_ASYNC_UNLINK;
+               usb_unlink_urb(&pegasus->ctrl_urb);
+       }
        if ( pegasus->rx_urb.status == -EINPROGRESS )
                usb_unlink_urb(&pegasus->rx_urb);
-       if ( pegasus->tx_urb.status == -EINPROGRESS )
+       if ( pegasus->tx_urb.status == -EINPROGRESS ) {
+               pegasus->tx_urb.transfer_flags &= ~USB_ASYNC_UNLINK;
                usb_unlink_urb(&pegasus->tx_urb);
+       }
        if ( pegasus->intr_urb.status == -EINPROGRESS )
                usb_unlink_urb(&pegasus->intr_urb);
 
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to