patch-2.2.0-pre9 linux/drivers/net/fmv18x.c

Next file: linux/drivers/net/irda/Config.in
Previous file: linux/drivers/net/at1700.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.0-pre8/linux/drivers/net/fmv18x.c linux/drivers/net/fmv18x.c
@@ -32,7 +32,7 @@
 */
 
 static const char *version =
-	"fmv18x.c:v1.3.71e 03/04/96  Yutaka TAMIYA (tamy@flab.fujitsu.co.jp)\n";
+	"fmv18x.c:v2.2.0 09/24/98  Yutaka TAMIYA (tamy@flab.fujitsu.co.jp)\n";
 
 #include <linux/module.h>
 
@@ -74,6 +74,8 @@
 	struct net_device_stats stats;
 	long open_time;				/* Useless example local info. */
 	uint tx_started:1;			/* Number of packet on the Tx queue. */
+	uint tx_queue_ready:1;		/* Tx queue is ready to be sent. */
+	uint rx_started:1;			/* Packets are Rxing. */
 	uchar tx_queue;				/* Number of packet on the Tx queue. */
 	ushort tx_queue_len;		/* Current length of the Tx queue. */
 };
@@ -92,7 +94,7 @@
 /* Run-time register bank 2 definitions. */
 #define DATAPORT		8		/* Word-wide DMA or programmed-I/O dataport. */
 #define TX_START		10
-#define COL16CNTL		11
+#define COL16CNTL		11		/* Controll Reg for 16 collisions */
 #define MODE13			13
 /* Fujitsu FMV-18x Card Configuration */
 #define	FJ_STATUS0		0x10
@@ -164,6 +166,7 @@
 __initfunc(int fmv18x_probe1(struct device *dev, short ioaddr))
 {
 	char irqmap[4] = {3, 7, 10, 15};
+	char irqmap_pnp[8] = {3, 4, 5, 7, 9, 10, 11, 15};
 	unsigned int i, irq;
 
 	/* Resetting the chip doesn't reset the ISA interface, so don't bother.
@@ -171,13 +174,26 @@
 	   */
 
 	/* Check I/O address configuration and Fujitsu vendor code */
-	if (fmv18x_probe_list[inb(ioaddr + FJ_CONFIG0) & 0x07] != ioaddr
- 	||  inb(ioaddr+FJ_MACADDR  ) != 0x00
+	if (inb(ioaddr+FJ_MACADDR  ) != 0x00
 	||  inb(ioaddr+FJ_MACADDR+1) != 0x00
 	||  inb(ioaddr+FJ_MACADDR+2) != 0x0e)
 		return -ENODEV;
 
-	irq = irqmap[(inb(ioaddr + FJ_CONFIG0)>>6) & 0x03];
+	/* Check PnP mode for FMV-183/184/183A/184A. */
+	/* This PnP routine is very poor. IO and IRQ should be known. */
+	if (inb(ioaddr + FJ_STATUS1) & 0x20) {
+		irq = dev->irq;
+		for (i = 0; i < 8; i++) {
+			if (irq == irqmap_pnp[i])
+				break;
+		}
+		if (i == 8)
+			return -ENODEV;
+	} else {
+		if (fmv18x_probe_list[inb(ioaddr + FJ_CONFIG0) & 0x07] != ioaddr)
+			return -ENODEV;
+		irq = irqmap[(inb(ioaddr + FJ_CONFIG0)>>6) & 0x03];
+	}
 
 	/* Snarf the interrupt vector now. */
 	if (request_irq(irq, &net_interrupt, 0, "fmv18x", dev)) {
@@ -247,6 +263,7 @@
 	/* Switch to bank 2 and lock our I/O address. */
 	outb(0x08, ioaddr + CONFIG_1);
 	outb(dev->if_port, ioaddr + MODE13);
+	outb(0x00, ioaddr + COL16CNTL);
 
 	if (net_debug)
 		printk(version);
@@ -283,6 +300,8 @@
 	outb(0xe8, ioaddr + CONFIG_1);
 
 	lp->tx_started = 0;
+	lp->tx_queue_ready = 1;
+	lp->rx_started = 0;
 	lp->tx_queue = 0;
 	lp->tx_queue_len = 0;
 
@@ -364,14 +383,20 @@
 			printk("%s: Transmitting a packet of length %lu.\n", dev->name,
 				   (unsigned long)skb->len);
 
-		/* Disable both interrupts. */
-		outw(0x0000, ioaddr + TX_INTR);
-
-		outw(length, ioaddr + DATAPORT);
-		outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+		/* We may not start transmitting unless we finish transferring
+		   a packet into the Tx queue. During executing the following
+		   codes we possibly catch a Tx interrupt. Thus we flag off
+		   tx_queue_ready, so that we prevent the interrupt routine
+		   (net_interrupt) to start transmitting. */
+		lp->tx_queue_ready = 0;
+		{
+			outw(length, ioaddr + DATAPORT);
+			outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
 
-		lp->tx_queue++;
-		lp->tx_queue_len += length + 2;
+			lp->tx_queue++;
+			lp->tx_queue_len += length + 2;
+		}
+		lp->tx_queue_ready = 1;
 
 		if (lp->tx_started == 0) {
 			/* If the Tx is idle, always trigger a transmit. */
@@ -384,9 +409,6 @@
 		} else if (lp->tx_queue_len < 4096 - 1502)
 			/* Yes, there is room for one more packet. */
 			dev->tbusy = 0;
-
-		/* Re-enable interrupts */
-		outw(0x8182, ioaddr + TX_INTR);
 	}
 	dev_kfree_skb (skb);
 
@@ -410,23 +432,37 @@
 
 	ioaddr = dev->base_addr;
 	lp = (struct net_local *)dev->priv;
-
-	/* Avoid multiple interrupts. */
-	outw(0x0000, ioaddr + TX_INTR);
-
-        status = inw(ioaddr + TX_STATUS);
+	status = inw(ioaddr + TX_STATUS);
 	outw(status, ioaddr + TX_STATUS);
 
 	if (net_debug > 4)
 		printk("%s: Interrupt with status %04x.\n", dev->name, status);
-	if (status & 0xff00
-		||  (inb(ioaddr + RX_MODE) & 0x40) == 0) {			/* Got a packet(s). */
+	if (lp->rx_started == 0 &&
+		(status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) {
+		/* Got a packet(s).
+		   We cannot execute net_rx more than once at the same time for
+		   the same device. During executing net_rx, we possibly catch a
+		   Tx interrupt. Thus we flag on rx_started, so that we prevent
+		   the interrupt routine (net_interrupt) to dive into net_rx
+		   again. */
+		lp->rx_started = 1;
+		outb(0x00, ioaddr + RX_INTR);	/* Disable RX intr. */
 		net_rx(dev);
+		outb(0x81, ioaddr + RX_INTR);	/* Enable  RX intr. */
+		lp->rx_started = 0;
 	}
 	if (status & 0x00ff) {
-		if (status & 0x80) {
+		if (status & 0x02) {
+			/* More than 16 collisions occurred */
+			if (net_debug > 4)
+				printk("%s: 16 Collision occur during Txing.\n", dev->name);
+			/* Cancel sending a packet. */
+			outb(0x03, ioaddr + COL16CNTL);
+			lp->stats.collisions++;
+		}
+		if (status & 0x82) {
 			lp->stats.tx_packets++;
-			if (lp->tx_queue) {
+			if (lp->tx_queue && lp->tx_queue_ready) {
 				outb(0x80 | lp->tx_queue, ioaddr + TX_START);
 				lp->tx_queue = 0;
 				lp->tx_queue_len = 0;
@@ -439,16 +475,9 @@
 				mark_bh(NET_BH);	/* Inform upper layers. */
 			}
 		}
-		if (status & 0x02 ) {
-			if (net_debug > 4)
-				printk("%s: 16 Collision occur during Txing.\n", dev->name);
-			/* Retry to send the packet */
-			outb(0x02, ioaddr + COL16CNTL);
-		}
 	}
 
 	dev->interrupt = 0;
-	outw(0x8182, ioaddr + TX_INTR);
 	return;
 }
 
@@ -458,7 +487,7 @@
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	int boguscount = 10;	/* 5 -> 10: by agy 19940922 */
+	int boguscount = 5;
 
 	while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
 		/* Clear PKT_RDY bit: by agy 19940922 */
@@ -619,6 +648,7 @@
 
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
+MODULE_PARM(net_debug, "i");
 
 int init_module(void)
 {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov