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]