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

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)