patch-2.2.13 linux/drivers/net/3c527.c
Next file: linux/drivers/net/3c59x.c
Previous file: linux/drivers/net/3c509.c
Back to the patch index
Back to the overall index
- Lines: 243
- Date:
Tue Oct 19 17:14:01 1999
- Orig file:
v2.2.12/linux/drivers/net/3c527.c
- Orig date:
Sun Mar 21 07:11:36 1999
diff -u --recursive --new-file v2.2.12/linux/drivers/net/3c527.c linux/drivers/net/3c527.c
@@ -1,3 +1,4 @@
+
/* 3c527.c: 3Com Etherlink/MC32 driver for Linux
*
* (c) Copyright 1998 Red Hat Software Inc
@@ -15,7 +16,7 @@
*/
static const char *version =
- "3c527.c:v0.04 1999/03/16 Alan Cox (alan@redhat.com)\n";
+ "3c527.c:v0.06 1999/09/16 Alan Cox (alan@redhat.com)\n";
/*
* Things you need
@@ -108,6 +109,7 @@
u16 tx_skb_end;
struct sk_buff *rx_skb[RX_RING_MAX]; /* Receive ring */
void *rx_ptr[RX_RING_MAX]; /* Data pointers */
+ u32 mc_list_valid; /* True when the mclist is set */
};
/* The station (ethernet) address prefix, used for a sanity check. */
@@ -138,6 +140,7 @@
static int mc32_close(struct device *dev);
static struct net_device_stats *mc32_get_stats(struct device *dev);
static void mc32_set_multicast_list(struct device *dev);
+static void mc32_reset_multicast_list(struct device *dev);
/*
* Check for a network adaptor of this type, and return '0' iff one exists.
@@ -442,19 +445,78 @@
/*
- * Send exec commands
+ * Send exec commands. This requires a bit of explaining.
+ *
+ * You feed the card a command, you wait, it interrupts you get a
+ * reply. All well and good. The complication arises because you use
+ * commands for filter list changes which come in at bh level from things
+ * like IPV6 group stuff.
+ *
+ * We have a simple state machine
+ *
+ * 0 - nothing issued
+ * 1 - command issued, wait reply
+ * 2 - reply waiting - reader then goes to state 0
+ * 3 - command issued, trash reply. In which case the irq
+ * takes it back to state 0
*/
+
+/*
+ * Send command from interrupt state
+ */
+
+static int mc32_command_nowait(struct device *dev, u16 cmd, void *data, int len)
+{
+ struct mc32_local *lp = (struct mc32_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if(lp->exec_pending)
+ return -1;
+
+ lp->exec_pending=3;
+ lp->exec_box->mbox=0;
+ lp->exec_box->mbox=cmd;
+ memcpy((void *)lp->exec_box->data, data, len);
+ barrier(); /* the memcpy forgot the volatile so be sure */
+
+ /* Send the command */
+ while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
+ outb(1<<6, ioaddr+HOST_CMD);
+ return 0;
+}
+
+
+/*
+ * Send command and block for results. On completion spot and reissue
+ * multicasts
+ */
+
static int mc32_command(struct device *dev, u16 cmd, void *data, int len)
{
struct mc32_local *lp = (struct mc32_local *)dev->priv;
int ioaddr = dev->base_addr;
unsigned long flags;
+ int ret = 0;
+ /*
+ * Wait for a command
+ */
+
+ save_flags(flags);
+ cli();
+
while(lp->exec_pending)
sleep_on(&lp->event);
+ /*
+ * Issue mine
+ */
+
lp->exec_pending=1;
+
+ restore_flags(flags);
+
lp->exec_box->mbox=0;
lp->exec_box->mbox=cmd;
memcpy((void *)lp->exec_box->data, data, len);
@@ -471,18 +533,20 @@
lp->exec_pending=0;
restore_flags(flags);
+
+ if(lp->exec_box->data[0]&(1<<13))
+ ret = -1;
/*
* A multicast set got blocked - do it now
*/
-
+
if(lp->mc_reload_wait)
- mc32_set_multicast_list(dev);
+ mc32_reset_multicast_list(dev);
- if(lp->exec_box->data[0]&(1<<13))
- return -1;
- return 0;
+ return ret;
}
+
/*
* RX abort
*/
@@ -779,6 +843,10 @@
wmb();
np->length = skb->len;
+
+ if(np->length < 60)
+ np->length = 60;
+
np->data = virt_to_bus(skb->data);
np->status = 0;
np->control = (1<<7)|(1<<6); /* EOP EOL */
@@ -968,8 +1036,11 @@
status>>=3;
if(status&1)
{
- /* 0=no 1=yes 2=reply clearing */
- lp->exec_pending=2;
+ /* 0=no 1=yes 2=replied, get cmd, 3 = wait reply & dump it */
+ if(lp->exec_pending!=3)
+ lp->exec_pending=2;
+ else
+ lp->exec_pending=0;
wake_up(&lp->event);
}
if(status&2)
@@ -1062,20 +1133,18 @@
* num_addrs > 0 Multicast mode, receive normal and MC packets,
* and do best-effort filtering.
*/
-static void mc32_set_multicast_list(struct device *dev)
+static void do_mc32_set_multicast_list(struct device *dev, int retry)
{
+ struct mc32_local *lp = (struct mc32_local *)dev->priv;
u16 filt;
+
if (dev->flags&IFF_PROMISC)
- {
/* Enable promiscuous mode */
filt = 1;
- mc32_command(dev, 0, &filt, 2);
- }
else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > 10)
{
dev->flags|=IFF_PROMISC;
filt = 1;
- mc32_command(dev, 0, &filt, 2);
}
else if(dev->mc_count)
{
@@ -1086,24 +1155,47 @@
int i;
filt = 0;
- block[1]=0;
- block[0]=dev->mc_count;
- bp=block+2;
- for(i=0;i<dev->mc_count;i++)
+ if(retry==0)
+ lp->mc_list_valid = 0;
+ if(!lp->mc_list_valid)
{
- memcpy(bp, dmc->dmi_addr, 6);
- bp+=6;
- dmc=dmc->next;
+ block[1]=0;
+ block[0]=dev->mc_count;
+ bp=block+2;
+
+ for(i=0;i<dev->mc_count;i++)
+ {
+ memcpy(bp, dmc->dmi_addr, 6);
+ bp+=6;
+ dmc=dmc->next;
+ }
+ if(mc32_command_nowait(dev, 2, block, 2+6*dev->mc_count)==-1)
+ {
+ lp->mc_reload_wait = 1;
+ return;
+ }
+ lp->mc_list_valid=1;
}
- mc32_command(dev, 2, block, 2+6*dev->mc_count);
- mc32_command(dev, 0, &filt, 2);
}
else
{
filt = 0;
- mc32_command(dev, 0, &filt, 2);
}
+ if(mc32_command_nowait(dev, 0, &filt, 2)==-1)
+ {
+ lp->mc_reload_wait = 1;
+ }
+}
+
+static void mc32_set_multicast_list(struct device *dev)
+{
+ do_mc32_set_multicast_list(dev,0);
+}
+
+static void mc32_reset_multicast_list(struct device *dev)
+{
+ do_mc32_set_multicast_list(dev,1);
}
#ifdef MODULE
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)