patch-2.3.43 linux/drivers/net/3c501.c

Next file: linux/drivers/net/3c503.c
Previous file: linux/drivers/macintosh/via-pmu.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.42/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
@@ -29,54 +29,64 @@
     with a TX-TX optimisation to see if we can touch 180-200K/second as seems
     theoretically maximum.
     		19950402 Alan Cox <Alan.Cox@linux.org>
+    		
+    Cleaned up for 2.3.x because we broke SMP now. 
+    		20000208 Alan Cox <alan@redhat.com>
+    		
+*/
 
-    Some notes on this thing if you have to hack it.  [Alan]
-
-    1]	Some documentation is available from 3Com. Due to the boards age
-    	standard responses when you ask for this will range from 'be serious'
-    	to 'give it to a museum'. The documentation is incomplete and mostly
-    	of historical interest anyway.
-
-    2]  The basic system is a single buffer which can be used to receive or
-    	transmit a packet. A third command mode exists when you are setting
-    	things up.
-
-    3]	If it's transmitting it's not receiving and vice versa. In fact the
-    	time to get the board back into useful state after an operation is
-    	quite large.
-
-    4]	The driver works by keeping the board in receive mode waiting for a
-    	packet to arrive. When one arrives it is copied out of the buffer
-    	and delivered to the kernel. The card is reloaded and off we go.
-
-    5]	When transmitting dev->tbusy is set and the card is reset (from
-    	receive mode) [possibly losing a packet just received] to command
-    	mode. A packet is loaded and transmit mode triggered. The interrupt
-    	handler runs different code for transmit interrupts and can handle
-    	returning to receive mode or retransmissions (yes you have to help
-    	out with those too).
-
-    Problems:
-    	There are a wide variety of undocumented error returns from the card
-    and you basically have to kick the board and pray if they turn up. Most
-    only occur under extreme load or if you do something the board doesn't
-    like (eg touching a register at the wrong time).
-
-    	The driver is less efficient than it could be. It switches through
-    receive mode even if more transmits are queued. If this worries you buy
-    a real Ethernet card.
-
-    	The combination of slow receive restart and no real multicast
-    filter makes the board unusable with a kernel compiled for IP
-    multicasting in a real multicast environment. That's down to the board,
-    but even with no multicast programs running a multicast IP kernel is
-    in group 224.0.0.1 and you will therefore be listening to all multicasts.
-    One nv conference running over that Ethernet and you can give up.
 
-*/
+/**
+ * DOC: 3c501 Card Notes
+ *
+ *  Some notes on this thing if you have to hack it.  [Alan]
+ *
+ *  Some documentation is available from 3Com. Due to the boards age
+ *  standard responses when you ask for this will range from 'be serious'
+ *  to 'give it to a museum'. The documentation is incomplete and mostly
+ *  of historical interest anyway. 
+ *
+ *  The basic system is a single buffer which can be used to receive or
+ *  transmit a packet. A third command mode exists when you are setting
+ *  things up.
+ *
+ *  If it's transmitting it's not receiving and vice versa. In fact the
+ *  time to get the board back into useful state after an operation is
+ *  quite large.
+ *
+ *  The driver works by keeping the board in receive mode waiting for a
+ *  packet to arrive. When one arrives it is copied out of the buffer
+ *  and delivered to the kernel. The card is reloaded and off we go.
+ *
+ *  When transmitting lp->txing is set and the card is reset (from
+ *  receive mode) [possibly losing a packet just received] to command
+ *  mode. A packet is loaded and transmit mode triggered. The interrupt
+ *  handler runs different code for transmit interrupts and can handle
+ *  returning to receive mode or retransmissions (yes you have to help
+ *  out with those too).
+ *
+ * DOC: Problems
+ *  
+ *  There are a wide variety of undocumented error returns from the card
+ *  and you basically have to kick the board and pray if they turn up. Most
+ *  only occur under extreme load or if you do something the board doesn't
+ *  like (eg touching a register at the wrong time).
+ *
+ *  The driver is less efficient than it could be. It switches through
+ *  receive mode even if more transmits are queued. If this worries you buy
+ *  a real Ethernet card.
+ *
+ *  The combination of slow receive restart and no real multicast
+ *  filter makes the board unusable with a kernel compiled for IP
+ *  multicasting in a real multicast environment. That's down to the board,
+ *  but even with no multicast programs running a multicast IP kernel is
+ *  in group 224.0.0.1 and you will therefore be listening to all multicasts.
+ *  One nv conference running over that Ethernet and you can give up.
+ *
+ */
 
 static const char *version =
-    "3c501.c: 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov).\n";
+    "3c501.c: 2000/02/08 Alan Cox (alan@redhat.com).\n";
 
 /*
  *	Braindamage remaining:
@@ -119,6 +129,7 @@
 int el1_probe(struct net_device *dev);
 static int  el1_probe1(struct net_device *dev, int ioaddr);
 static int  el_open(struct net_device *dev);
+static void el_timeout(struct net_device *dev);
 static int  el_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static void el_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void el_receive(struct net_device *dev);
@@ -144,6 +155,7 @@
 	int		tx_pkt_start;	/* The length of the current Tx packet. */
 	int		collisions;	/* Tx collisions this packet */
 	int		loading;	/* Spot buffer load collisions */
+	int		txing;		/* True if card is in TX mode */
 	spinlock_t	lock;		/* Serializing lock */
 };
 
@@ -210,6 +222,19 @@
 struct netdev_entry el1_drv = {"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist};
 #else
 
+/**
+ * el1_probe:
+ * @dev: The device structure passed in to probe. 
+ *
+ * This can be called from two places. The network layer will probe using
+ * a device structure passed in with the probe information completed. For a
+ * modular driver we use #init_module to fill in our own structure and probe
+ * for it.
+ *
+ * Returns 0 on success. ENXIO if asked not to probe and ENODEV if asked to
+ * probe and failing to find anything.
+ */
+ 
 int __init el1_probe(struct net_device *dev)
 {
 	int i;
@@ -233,8 +258,17 @@
 }
 #endif
 
-/*
- *	The actual probe.
+/**
+ *	el1_probe: 
+ *	@dev: The device structure to use
+ *	@ioaddr: An I/O address to probe at.
+ *
+ *	The actual probe. This is iterated over by #el1_probe in order to
+ *	check all the applicable device locations.
+ *
+ *	Returns 0 for a success, in which case the device is activated,
+ *	EAGAIN if the IRQ is in use by another driver, and ENODEV if the
+ *	board cannot be found.
  */
 
 static int __init el1_probe1(struct net_device *dev, int ioaddr)
@@ -310,11 +344,11 @@
 	if (autoirq)
 		dev->irq = autoirq;
 
-	printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n", dev->name, mname, dev->base_addr,
+	printk(KERN_INFO "%s: %s EtherLink at %#lx, using %sIRQ %d.\n", dev->name, mname, dev->base_addr,
 			autoirq ? "auto":"assigned ", dev->irq);
 
 #ifdef CONFIG_IP_MULTICAST
-	printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
+	printk(KERN_WARNING "WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
 #endif
 
 	if (el_debug)
@@ -338,6 +372,8 @@
 
 	dev->open = &el_open;
 	dev->hard_start_xmit = &el_start_xmit;
+	dev->tx_timeout = &el_timeout;
+	dev->watchdog_timeo = HZ;
 	dev->stop = &el1_close;
 	dev->get_stats = &el1_get_stats;
 	dev->set_multicast_list = &set_multicast_list;
@@ -351,13 +387,24 @@
 	return 0;
 }
 
-/*
- *	Open/initialize the board.
+/**
+ *	el1_open:
+ *	@dev: device that is being opened
+ *
+ *	When an ifconfig is issued which changes the device flags to include
+ *	IFF_UP this function is called. It is only called when the change 
+ *	occurs, not when the interface remains up. #el1_close will be called
+ *	when it goes down.
+ *
+ *	Returns 0 for a successful open, or -EAGAIN if someone has run off
+ *	with our interrupt line.
  */
 
 static int el_open(struct net_device *dev)
 {
 	int ioaddr = dev->base_addr;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	unsigned long flags;
 
 	if (el_debug > 2)
 		printk("%s: Doing el_open()...", dev->name);
@@ -365,47 +412,75 @@
 	if (request_irq(dev->irq, &el_interrupt, 0, "3c501", dev))
 		return -EAGAIN;
 
+	spin_lock_irqsave(&lp->lock, flags);
 	el_reset(dev);
+	spin_unlock_irqrestore(&lp->lock, flags);
 
-	dev->start = 1;
-
+	lp->txing = 0;		/* Board in RX mode */
 	outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */
+	netif_start_queue(dev);
 	MOD_INC_USE_COUNT;
 	return 0;
 }
 
-static int el_start_xmit(struct sk_buff *skb, struct net_device *dev)
+/**
+ * el_timeout:
+ * @dev: The 3c501 card that has timed out
+ *
+ * Attempt to restart the board. This is basically a mixture of extreme
+ * violence and prayer
+ *
+ */
+ 
+static void el_timeout(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	unsigned long flags;
+ 
+	if (el_debug)
+		printk (KERN_DEBUG "%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
+			dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
+	lp->stats.tx_errors++;
+	outb(TX_NORM, TX_CMD);
+	outb(RX_NORM, RX_CMD);
+	outb(AX_OFF, AX_CMD);	/* Just trigger a false interrupt. */
+	outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */
+	lp->txing = 0;		/* Ripped back in to RX */
+	netif_wake_queue(dev);
+}
 
-	if(dev->interrupt)		/* May be unloading, don't stamp on */
-		return 1;		/* the packet buffer this time      */
+ 
+/**
+ * el_start_xmit:
+ * @skb: The packet that is queued to be sent
+ * @dev: The 3c501 card we want to throw it down
+ *
+ * Attempt to send a packet to a 3c501 card. There are some interesting
+ * catches here because the 3c501 is an extremely old and therefore
+ * stupid piece of technology.
+ *
+ * If we are handling an interrupt on the other CPU we cannot load a packet
+ * as we may still be attempting to retrieve the last RX packet buffer.
+ *
+ * When a transmit times out we dump the card into control mode and just
+ * start again. It happens enough that it isnt worth logging.
+ *
+ * We avoid holding the spin locks when doing the packet load to the board.
+ * The device is very slow, and its DMA mode is even slower. If we held the
+ * lock while loading 1500 bytes onto the controller we would drop a lot of
+ * serial port characters. This requires we do extra locking, but we have
+ * no real choice.
+ */
 
-	if (dev->tbusy)
-	{
-		if (jiffies - dev->trans_start < 20)
-		{
-			if (el_debug > 2)
-				printk(" transmitter busy, deferred.\n");
-			return 1;
-		}
-		if (el_debug)
-			printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
-				dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
-		lp->stats.tx_errors++;
-		outb(TX_NORM, TX_CMD);
-		outb(RX_NORM, RX_CMD);
-		outb(AX_OFF, AX_CMD);	/* Just trigger a false interrupt. */
-		outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */
-		dev->tbusy = 0;
-		dev->trans_start = jiffies;
-	}
+static int el_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	unsigned long flags;
 
 	/*
-	 *	Avoid incoming interrupts between us flipping tbusy and flipping
-	 *	mode as the driver assumes tbusy is a faithful indicator of card
+	 *	Avoid incoming interrupts between us flipping txing and flipping
+	 *	mode as the driver assumes txing is a faithful indicator of card
 	 *	state
 	 */
 
@@ -415,17 +490,13 @@
 	 *	Avoid timer-based retransmission conflicts.
 	 */
 
-	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
-	{
-		spin_unlock_irqrestore(&lp->lock, flags);
-		printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
-	}
-	else
+	netif_stop_queue(dev);
+
+	do
 	{
 		int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
 		unsigned char *buf = skb->data;
 
-load_it_again_sam:
 		lp->tx_pkt_start = gp_start;
     		lp->collisions = 0;
 
@@ -440,7 +511,8 @@
 		inb_p(RX_STATUS);
 		inb_p(TX_STATUS);
 
-		lp->loading=1;
+		lp->loading = 1;
+		lp->txing = 1;
 
 		/*
 		 *	Turn interrupts back on while we spend a pleasant afternoon
@@ -453,29 +525,48 @@
 		outw(gp_start, GP_LOW);		/* aim - packet will be loaded into buffer start */
 		outsb(DATAPORT,buf,skb->len);	/* load buffer (usual thing each byte increments the pointer) */
 		outw(gp_start, GP_LOW);		/* the board reuses the same register */
-
-		if(lp->loading==2)		/* A receive upset our load, despite our best efforts */
+	
+		if(lp->loading != 2)
 		{
-			if(el_debug>2)
-				printk("%s: burped during tx load.\n", dev->name);
-			spin_lock_irqsave(&lp->lock, flags);
-			goto load_it_again_sam;	/* Sigh... */
+			outb(AX_XMIT, AX_CMD);		/* fire ... Trigger xmit.  */
+			lp->loading=0;
+			dev->trans_start = jiffies;
+			if (el_debug > 2)
+				printk(" queued xmit.\n");
+			dev_kfree_skb (skb);
+			return 0;
 		}
-		outb(AX_XMIT, AX_CMD);		/* fire ... Trigger xmit.  */
-		lp->loading=0;
-		dev->trans_start = jiffies;
+		/* A receive upset our load, despite our best efforts */
+		if(el_debug>2)
+			printk("%s: burped during tx load.\n", dev->name);
+		spin_lock_irqsave(&lp->lock, flags);
 	}
+	while(1);
 
-	if (el_debug > 2)
-		printk(" queued xmit.\n");
-	dev_kfree_skb (skb);
-	return 0;
 }
 
 
-/*
- *	The typical workload of the driver:
- *	Handle the ether interface interrupts.
+/**
+ * el_interrupt:
+ * @irq: Interrupt number
+ * @dev_id: The 3c501 that burped
+ * @regs: Register data (surplus to our requirements)
+ *
+ * Handle the ether interface interrupts. The 3c501 needs a lot more 
+ * hand holding than most cards. In paticular we get a transmit interrupt
+ * with a collision error because the board firmware isnt capable of rewinding
+ * its own transmit buffer pointers. It can however count to 16 for us.
+ *
+ * On the receive side the card is also very dumb. It has no buffering to
+ * speak of. We simply pull the packet out of its PIO buffer (which is slow)
+ * and queue it for the kernel. Then we reset the card for the next packet.
+ *
+ * We sometimes get suprise interrupts late both because the SMP IRQ delivery
+ * is message passing and because the card sometimes seems to deliver late. I
+ * think if it is part way through a receive and the mode is changed it carries
+ * on receiving and sends us an interrupt. We have to band aid all these cases
+ * to get a sensible 150kbytes/second performance. Even then you want a small
+ * TCP window.
  */
 
 static void el_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -485,12 +576,6 @@
 	int ioaddr;
 	int axsr;			/* Aux. status reg. */
 
-	if (dev == NULL  ||  dev->irq != irq)
-	{
-		printk (KERN_ERR "3c501 driver: irq %d for unknown device.\n", irq);
-		return;
-	}
-
 	ioaddr = dev->base_addr;
 	lp = (struct net_local *)dev->priv;
 
@@ -508,14 +593,12 @@
 
 	if (el_debug > 3)
 		printk(KERN_DEBUG "%s: el_interrupt() aux=%#02x", dev->name, axsr);
-	if (dev->interrupt)
-		printk(KERN_WARNING "%s: Reentering the interrupt driver!\n", dev->name);
-	dev->interrupt = 1;
-        if(lp->loading==1 && !dev->tbusy)
+
+        if(lp->loading==1 && !lp->txing)
         	printk(KERN_WARNING "%s: Inconsistent state loading while not in tx\n",
         		dev->name);
 
-	if (dev->tbusy)
+	if (lp->txing)
 	{
 
     		/*
@@ -533,7 +616,6 @@
 				printk(" txsr=%02x gp=%04x rp=%04x]\n", txsr, inw(GP_LOW),inw(RX_LOW));
 			}
 			lp->loading=2;		/* Force a reload */
-			dev->interrupt = 0;
 			spin_unlock(&lp->lock);
 			return;
 		}
@@ -551,8 +633,8 @@
 				printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
 			  		" gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
 			inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
-			dev->tbusy = 0;
-			mark_bh(NET_BH);
+			lp->txing = 0;
+			netif_wake_queue(dev);
 		}
 		else if (txsr & TX_16COLLISIONS)
 		{
@@ -562,7 +644,9 @@
 			if (el_debug)
 				printk("%s: Transmit failed 16 times, Ethernet jammed?\n",dev->name);
 			outb(AX_SYS, AX_CMD);
+			lp->txing = 0;
 			lp->stats.tx_aborted_errors++;
+			netif_wake_queue(dev);
 		}
 		else if (txsr & TX_COLLISION)
 		{
@@ -580,7 +664,6 @@
 			outw(lp->tx_pkt_start, GP_LOW);
 			outb(AX_XMIT, AX_CMD);
 			lp->stats.collisions++;
-			dev->interrupt = 0;
 			spin_unlock(&lp->lock);
 			return;
 		}
@@ -597,8 +680,8 @@
 			 *	This is safe the interrupt is atomic WRT itself.
 			 */
 
-			dev->tbusy = 0;
-			mark_bh(NET_BH);	/* In case more to transmit */
+			lp->txing = 0;
+			netif_wake_queue(dev);	/* In case more to transmit */
 		}
 	}
 	else
@@ -650,15 +733,19 @@
 	outw(0x00, RX_BUF_CLR);
 	inb(RX_STATUS);		/* Be certain that interrupts are cleared. */
 	inb(TX_STATUS);
-	dev->interrupt = 0;
 	spin_unlock(&lp->lock);
 	return;
 }
 
 
-/*
- *	We have a good packet. Well, not really "good", just mostly not broken.
- *	We must check everything to see if it is good.
+/**
+ * el_receive:
+ * @dev: Device to pull the packets from
+ *
+ * We have a good packet. Well, not really "good", just mostly not broken.
+ * We must check everything to see if it is good. In paticular we occasionally
+ * get wild packet sizes from the card. If the packet seems sane we PIO it
+ * off the card and queue it for the protocol layers.
  */
 
 static void el_receive(struct net_device *dev)
@@ -717,8 +804,18 @@
 	return;
 }
 
+/**
+ * el_reset: Reset a 3c501 card
+ * @dev: The 3c501 card about to get zapped
+ *
+ * Even resetting a 3c501 isnt simple. When you activate reset it loses all
+ * its configuration. You must hold the lock when doing this. The function
+ * cannot take the lock itself as it is callable from the irq handler.
+ */
+
 static void  el_reset(struct net_device *dev)
 {
+	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 
 	if (el_debug> 2)
@@ -732,16 +829,24 @@
 	}
 
 	outw(0, RX_BUF_CLR);		/* Set rx packet area to 0. */
-	cli();				/* Avoid glitch on writes to CMD regs */
 	outb(TX_NORM, TX_CMD);		/* tx irq on done, collision */
 	outb(RX_NORM, RX_CMD);		/* Set Rx commands. */
 	inb(RX_STATUS);			/* Clear status. */
 	inb(TX_STATUS);
-	dev->interrupt = 0;
-	dev->tbusy = 0;
-	sti();
+	lp->txing = 0;
 }
 
+/**
+ * el1_close:
+ * @dev: 3c501 card to shut down
+ *
+ * Close a 3c501 card. The IFF_UP flag has been cleared by the user via
+ * the SIOCSIFFLAGS ioctl. We stop any further transmissions being queued,
+ * and then disable the interrupts. Finally we reset the chip. The effects
+ * of the rest will be cleaned up by #el1_open. Always returns 0 indicating
+ * a success.
+ */
+ 
 static int el1_close(struct net_device *dev)
 {
 	int ioaddr = dev->base_addr;
@@ -749,9 +854,8 @@
 	if (el_debug > 2)
 		printk("%s: Shutting down Ethernet card at %#x.\n", dev->name, ioaddr);
 
-	dev->tbusy = 1;
-	dev->start = 0;
-
+	netif_stop_queue(dev);
+	
 	/*
 	 *	Free and disable the IRQ.
 	 */
@@ -763,15 +867,31 @@
 	return 0;
 }
 
+/**
+ * el1_get_stats:
+ * @dev: The card to get the statistics for
+ *
+ * In smarter devices this function is needed to pull statistics off the
+ * board itself. The 3c501 has no hardware statistics. We maintain them all
+ * so they are by definition always up to date.
+ *
+ * Returns the statistics for the card from the card private data
+ */
+ 
 static struct net_device_stats *el1_get_stats(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	return &lp->stats;
 }
 
-/*
- *	Set or clear the multicast filter for this adaptor.
- *			best-effort filtering.
+/**
+ * set_multicast_list:
+ * @dev: The device to adjust
+ *
+ * Set or clear the multicast filter for this adaptor to use the best-effort 
+ * filtering supported. The 3c501 supports only three modes of filtering.
+ * It always receives broadcasts and packets for itself. You can choose to
+ * optionally receive all packets, or all multicast packets on top of this.
  */
 
 static void set_multicast_list(struct net_device *dev)
@@ -812,6 +932,18 @@
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
 
+/**
+ * init_module:
+ *
+ * When the driver is loaded as a module this function is called. We fake up
+ * a device structure with the base I/O and interrupt set as if it was being
+ * called from Space.c. This minimises the extra code that would otherwise
+ * be required.
+ *
+ * Returns 0 for success or -EIO if a card is not found. Returning an error
+ * here also causes the module to be unloaded
+ */
+ 
 int init_module(void)
 {
 	dev_3c501.irq=irq;
@@ -821,6 +953,13 @@
 	return 0;
 }
 
+/**
+ * cleanup_module:
+ * 
+ * The module is being unloaded. We unhook our network device from the system
+ * and then free up the resources we took when the card was found.
+ */
+ 
 void cleanup_module(void)
 {
 	/*

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)