On Wed, 17 May 2000, Petko Manolov wrote:

> > I activated the pegasus_irq routine ; this gives strange results.
> > The eight bytes indeed contain the data I expected (in particular, txst0
> > and txst1, whith "probably good" values. When the adapter locks itself, it
> > returns a carrier loss (txst0 == 0x18).
> 
> Which is not right if course, the problem might be no response to the
> NAKs.

I *finally* got it running ! 

In kpegasusd, I simply do a pegasus_set_register(pegasus->usb,0x01,0x48)
whenever needed. I schedule that unlock when either the interrupt pipe
tells me (txst0 & 0x18) or the netdev watchdog isn't happy (I think the
latter case can safely be removed). (did that in kpegasusd, because I
couldn't issue pegasus_set_register() in the intr routine or in a
neighbour tasklet).

Here's a wrong patch against your .9 version, in raw form (without
cleanup), so that you could tell me whether you can reproduce it.

> > However, unlike what the manual says, those eight bytes and the counter in
> > particular are *not* reset upon EP3 access.
> That's one of the reasons i don't use interrupt pipe for now. It behaves
> weird!

Nope, it works (or seems to) just as advertised, but you need to enable
the auto-reset feature. Register 1, bit 0.

Well, kpegasusd is obviously a wrong approach. Isn't there another way,
similar to tasklets, to schedule a less-urgent task to be executed *out*
of interrupt context ? Given USB's semantics, I've got the feeling that
this could be a really useful facility.

        -- Cyrille

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

--- /tmp/pegasus.c      Wed May 17 09:28:30 2000
+++ drivers/usb/pegasus.c       Wed May 17 09:18:11 2000
@@ -14,16 +14,20 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/usb.h>
+#include <linux/interrupt.h>
+#include <linux/tqueue.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
 
-
-static const char *version = __FILE__ ": v0.3.9 2000/04/11 Written by Petko Manolov 
([EMAIL PROTECTED])\n";
+static const char *version = __FILE__ ": v0.3.9h 2000/04/11 Written by 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*3) /* was 5 */
+#define PEGASUS_MAX_RETRIES     5
 #define        ALIGN(x)                x __attribute__((aligned(L1_CACHE_BYTES)))
 
 struct pegasus {
@@ -32,6 +36,10 @@
        struct net_device_stats stats;
        spinlock_t              pegasus_lock;
        struct urb              rx_urb, tx_urb, intr_urb;
+       int                     tx_retry_num;
+
+       struct pegasus          *next_event; /* for retries */
+
        unsigned char           ALIGN(rx_buff[PEGASUS_MAX_MTU]); 
        unsigned char           ALIGN(tx_buff[PEGASUS_MAX_MTU]); 
        unsigned char           ALIGN(intr_buff[8]);
@@ -51,6 +59,12 @@
 MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
 MODULE_PARM(loopback, "i");
 
+static spinlock_t pegasus_event_lock = SPIN_LOCK_UNLOCKED;
+static struct pegasus *event_list = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(kpegasusd_wait);
+static int kpegasusd_pid = 0;
+static int kpegasusd_running = 0;
+
 
 static struct usb_eth_dev usb_dev_id[] = {
        {"Billionton USB-100", 0x08dd, 0x0986, NULL},
@@ -78,7 +92,6 @@
        { __u8  data = value;                   \
        usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, 
HZ);}
 
-
 static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
 {
        int i;
@@ -174,7 +187,7 @@
        if (pegasus_get_node_id(usb, node_id)) 
                return 1;
 
-       pegasus_set_registers(usb, 0x10, 6, node_id);
+       pegasus_set_registers(usb, 0x10, 6, node_id); /* Ether_ID */
        memcpy(dev->dev_addr, node_id, 6);
        if (pegasus_read_phy_word(usb, 1, &temp)) 
                return 2;
@@ -193,9 +206,14 @@
                /* return 5;    FIXME */ 
        }
 
-       data[0] = 0xc9;
+       data[0] = 0xc9; /* tx_enable, rx_enable, rxstatus_append, 
+                          include_crc */
        data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+       /* 0x30: full_duplex, 10Mode / Just 10Mode */
+
        data[2] = (loopback & 1) ? 0x08 : 0x00;
+       /* loopback or not */
+       data[2] |= 0x01; /* clear hw status after access. */
 
        pegasus_set_registers(usb, 0, 3, data);
 
@@ -255,10 +273,32 @@
 
 static void pegasus_irq(urb_t *urb)
 {
-       if(urb->status) {
+       if (!(urb->status)) {
                __u8    *d = urb->transfer_buffer;
-               printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x",
-                       d[0], d[1], d[2], d[3], d[4], d[5]);
+               __u8    data[2];
+               unsigned long flags;
+               struct pegasus *pegasus = urb->context;
+               /*printk("status %d txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, 
+wakest %x\n",
+                 urb->status, d[0], d[1], d[2], d[3], d[4], d[5]); */
+               if (d[0]) pegasus->stats.tx_errors++;
+               if (d[1] & 0x80) pegasus->stats.tx_fifo_errors++;
+               if (d[0] & 0x18) {
+                       /* Most certainly, the board is locked now. */
+                       warn("whoops, adapter reported a carrier loss !");
+                       pegasus->stats.tx_carrier_errors++;
+
+                       spin_lock_irqsave(&pegasus_event_lock,flags);
+                       pegasus->next_event = event_list;
+                       event_list = pegasus;
+                       
+                       wake_up(&kpegasusd_wait);
+
+                       spin_unlock_irqrestore(&pegasus_event_lock,flags);
+               }
+               /* This *might* save it ? */
+               /*pegasus_get_registers(pegasus->usb,0x2B,2,data);
+               pegasus_get_registers(pegasus->usb,0x2D,2,data);
+               warn("got %x %x \n",data[0],data[1]);*/
        }
 }
 
@@ -278,11 +318,31 @@
 static void pegasus_tx_timeout(struct net_device *net)
 {
        struct pegasus *pegasus = net->priv;
+       unsigned long flags;
 
        warn("%s: Tx timed out. Reseting...", net->name);
-       pegasus->stats.tx_errors++;
        net->trans_start = jiffies;
 
+       if (pegasus->tx_retry_num++ < PEGASUS_MAX_RETRIES) 
+       {
+               warn("Scheduling a retry");
+
+               if (waitqueue_active(&kpegasusd_wait)) {
+                       warn("(yes, really)");
+                       spin_lock_irqsave(&pegasus_event_lock,flags);
+                       pegasus->next_event = event_list;
+                       event_list = pegasus;
+                       
+                       wake_up(&kpegasusd_wait);
+
+                       spin_unlock_irqrestore(&pegasus_event_lock,flags);
+               }               
+               warn("done");
+       } else {
+               pegasus->stats.tx_errors++;
+               pegasus->stats.tx_aborted_errors++;
+       }
+
        netif_wake_queue(net);
 }
 
@@ -299,12 +359,24 @@
        ((__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);
                pegasus->stats.tx_errors++;
+               if (res == -EINVAL) {
+                       warn("invalid URB ! count %d, skb->len %d, "
+                            "now urb-status %d, "
+                            "retry_num %d",count,skb->len,
+                            pegasus->tx_urb.status,
+                            pegasus->tx_retry_num);
+                       if (pegasus->tx_urb.status == -EINPROGRESS) {
+                               warn("URB still in progress ! ");
+                       }
+               }
                netif_start_queue(net);
        } else {
+               pegasus->tx_retry_num = 0;
                pegasus->stats.tx_packets++;
                pegasus->stats.tx_bytes += skb->len;
                net->trans_start = jiffies;
@@ -413,11 +485,12 @@
        return  -1;
 }
 
-static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
+static void *pegasus_probe(struct usb_device *dev, unsigned int ifnum)
 {
        struct net_device *net;
        struct pegasus *pegasus;
        int     dev_indx;
+       int ret;
 
        if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, 
dev->descriptor.idProduct)) == -1 ) {
                return NULL;
@@ -466,6 +539,16 @@
                        pegasus->intr_buff, 8, pegasus_irq, pegasus, 250);
 
 
+       /* pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; */
+       pegasus->tx_retry_num = 0;
+       pegasus->next_event = NULL;
+
+
+       ret = usb_submit_urb(&pegasus->intr_urb);
+       if (!ret) {
+               warn("submit intr urb returned %d",ret);
+       }
+       
        printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name);
 
        return pegasus;
@@ -487,7 +570,7 @@
 
        usb_unlink_urb(&pegasus->rx_urb);
        usb_unlink_urb(&pegasus->tx_urb);
-/*     usb_unlink_urb(&pegasus->intr_urb);*/
+       usb_unlink_urb(&pegasus->intr_urb);
 
        kfree(pegasus);
 }
@@ -498,14 +581,93 @@
        disconnect:     pegasus_disconnect,
 };
 
+
+
+static void pegasus_events(void) {
+       unsigned long flags;
+       warn("in pegasus_events");
+       while (1) {
+               __u8 data;
+               struct pegasus *pegasus;
+               spin_lock_irqsave(&pegasus_event_lock,flags);
+               
+               if (!event_list)
+                       goto do_unlock;
+
+               pegasus = event_list;
+               event_list = pegasus->next_event;
+               spin_unlock_irqrestore(&pegasus_event_lock,flags);
+               
+               warn("ready to unlock %s",pegasus->net->name);
+               pegasus_set_register(pegasus->usb,0x01,0x48);
+               warn("unlock done");
+       }
+ do_unlock:
+       spin_unlock_irqrestore(&pegasus_event_lock,flags);
+}
+
+static int pegasus_thread(void *nothing)
+ {
+         kpegasusd_running = 1;
+         /*lock_kernel();*/
+
+         /*
+          * This thread doesn't need any user-level access,
+          * so get rid of all our resources
+          */
+         exit_files(current);  /* daemonize doesn't do exit_files */
+        daemonize();
+
+         /* Setup a nice name */
+         strcpy(current->comm, "kpegasusd");
+ 
+         /* Send me a signal to get me die (for debugging) */
+         do {
+                 pegasus_events();
+                 interruptible_sleep_on(&kpegasusd_wait);
+         } while (!signal_pending(current));
+ 
+         kpegasusd_running = 0;
+
+        return 0;
+ }
+
 int __init pegasus_init(void)
 {
+       int pid;
+
        printk( version );
-       return usb_register(&pegasus_driver);
+       if (usb_register(&pegasus_driver) < 0)
+               return -1;
+
+       pid = kernel_thread(pegasus_thread,NULL,
+                           CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+       if (pid >= 0) {
+               kpegasusd_pid = pid;
+               return 0;
+       }
+
+       usb_deregister(&pegasus_driver);
+       return -1;
 }
 
 void __exit pegasus_exit(void)
 {
+       int ret;
+       ret = kill_proc(kpegasusd_pid,SIGTERM,1);
+       if (!ret) {
+               /* wait 10 seconds */
+               int count = 10 * 100;
+               
+               while (kpegasusd_running && --count) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       schedule_timeout(1);
+               }
+               
+               if (!count) 
+                       err("giving up on killing kpegasusd");
+       }
+
        usb_deregister(&pegasus_driver);
 }
 
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to