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]