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]