patch-2.3.45 linux/drivers/net/atp.c

Next file: linux/drivers/net/atp.h
Previous file: linux/drivers/net/ariadne.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.44/linux/drivers/net/atp.c linux/drivers/net/atp.c
@@ -16,6 +16,8 @@
 		Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
 	The timer-based reset code was written by Bill Carlson, wwc@super.org.
+	
+	Modular support/softnet added by Alan Cox.
 */
 
 static const char *version =
@@ -82,6 +84,7 @@
 */
 
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -101,6 +104,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/spinlock.h>
 
 #include "atp.h"
 
@@ -133,9 +137,10 @@
 static unsigned short eeprom_op(short ioaddr, unsigned int cmd);
 static int net_open(struct net_device *dev);
 static void hardware_init(struct net_device *dev);
+static void tx_timeout(struct net_device *dev);
 static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
 static void trigger_send(short ioaddr, int length);
-static int	net_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
 static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void net_rx(struct net_device *dev);
 static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
@@ -150,8 +155,8 @@
    If dev->base_addr == 2, allocate space for the device and return success
    (detachable devices only).
    */
-int __init 
-atp_init(struct net_device *dev)
+
+int __init atp_init(struct net_device *dev)
 {
 	int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
 	int base_addr = dev->base_addr;
@@ -220,7 +225,7 @@
 		   dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
 
 	/* Leave the hardware in a reset state. */
-    write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
 
 	if (net_debug)
 		printk(version);
@@ -231,11 +236,10 @@
 	if (dev->priv == NULL)
 		return -ENOMEM;
 	memset(dev->priv, 0, sizeof(struct net_local));
-
-
 	{
 		struct net_local *lp = (struct net_local *)dev->priv;
 		lp->addr_mode = CMR2h_Normal;
+		spin_lock_init(&lp->lock);
 	}
 
 	/* For the ATP adapter the "if_port" is really the data transfer mode. */
@@ -245,9 +249,11 @@
 
 	dev->open		= net_open;
 	dev->stop		= net_close;
-	dev->hard_start_xmit = net_send_packet;
-	dev->get_stats	= net_get_stats;
-	dev->set_multicast_list = &set_multicast_list;
+	dev->hard_start_xmit 	= net_send_packet;
+	dev->get_stats		= net_get_stats;
+	dev->set_multicast_list = set_multicast_list;
+	dev->tx_timeout		= tx_timeout;
+	dev->watchdog_timeo	= HZ/20;
 
 #ifdef TIMED_CHECKER
 	del_timer(&atp_timer);
@@ -327,12 +333,12 @@
 	/* The interrupt line is turned off (tri-stated) when the device isn't in
 	   use.  That's especially important for "attached" interfaces where the
 	   port or interrupt may be shared. */
+	   
 	if (request_irq(dev->irq, &net_interrupt, 0, "ATP", dev)) {
 		return -EAGAIN;
 	}
-
 	hardware_init(dev);
-	dev->start = 1;
+	netif_start_queue(dev);
 	return 0;
 }
 
@@ -342,11 +348,11 @@
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-    int i;
+	int i;
 
 	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
 	
-    for (i = 0; i < 6; i++)
+	for (i = 0; i < 6; i++)
 		write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
 
 	write_reg_high(ioaddr, CMR2, lp->addr_mode);
@@ -356,22 +362,19 @@
 			   (read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f);
 	}
 
-    write_reg(ioaddr, CMR2, CMR2_IRQOUT);
-    write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+	write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+	write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
 
 	/* Enable the interrupt line from the serial port. */
 	outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
 
 	/* Unmask the interesting interrupts. */
-    write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
-    write_reg_high(ioaddr, IMR, ISRh_RxErr);
+	write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+	write_reg_high(ioaddr, IMR, ISRh_RxErr);
 
 	lp->tx_unit_busy = 0;
-    lp->pac_cnt_in_tx_buf = 0;
+	lp->pac_cnt_in_tx_buf = 0;
 	lp->saved_tx_size = 0;
-
-	dev->tbusy = 0;
-	dev->interrupt = 0;
 }
 
 static void trigger_send(short ioaddr, int length)
@@ -383,15 +386,15 @@
 
 static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
 {
-    length = (length + 1) & ~1;		/* Round up to word length. */
-    outb(EOC+MAR, ioaddr + PAR_DATA);
-    if ((data_mode & 1) == 0) {
+	length = (length + 1) & ~1;		/* Round up to word length. */
+	outb(EOC+MAR, ioaddr + PAR_DATA);
+	if ((data_mode & 1) == 0) {
 		/* Write the packet out, starting with the write addr. */
 		outb(WrAddr+MAR, ioaddr + PAR_DATA);
 		do {
 			write_byte_mode0(ioaddr, *packet++);
 		} while (--length > 0) ;
-    } else {
+	} else {
 		/* Write the packet out in slow mode. */
 		unsigned char outbyte = *packet++;
 
@@ -405,70 +408,61 @@
 		outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
 		while (--length > 0)
 			write_byte_mode1(ioaddr, *packet++);
-    }
-    /* Terminate the Tx frame.  End of write: ECB. */
-    outb(0xff, ioaddr + PAR_DATA);
-    outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+	}
+	/* Terminate the Tx frame.  End of write: ECB. */
+	outb(0xff, ioaddr + PAR_DATA);
+	outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
 }
 
-static int
-net_send_packet(struct sk_buff *skb, struct net_device *dev)
+static void tx_timeout(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
+	/* If we get here, some higher level has decided we are broken. */
+	printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+		   inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
+		   :  "IRQ conflict");
+	lp->stats.tx_errors++;
+	/* Try to restart the adapter. */
+	hardware_init(dev);
+	dev->trans_start = jiffies;
+	netif_wake_queue(dev);
+}
 
-	if (dev->tbusy) {
-		/* If we get here, some higher level has decided we are broken.
-		   There should really be a "kick me" function call instead. */
-		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 5)
-			return 1;
-		printk("%s: transmit timed out, %s?\n", dev->name,
-			   inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
-			   :  "IRQ conflict");
-		lp->stats.tx_errors++;
-		/* Try to restart the adapter. */
-		hardware_init(dev);
-		dev->tbusy=0;
-		dev->trans_start = jiffies;
-	}
+static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	unsigned char *buf = skb->data;
+	unsigned long flags;
+	
+	netif_stop_queue(dev);
+	
+	/* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
+	   This sequence must not be interrupted by an incoming packet. */
+	   
+	spin_lock_irqsave(&lp->lock, flags);
+	write_reg(ioaddr, IMR, 0);
+	write_reg_high(ioaddr, IMR, 0);
+	spin_unlock_irqrestore(&lp->lock, flags);
+	
+	write_packet(ioaddr, length, buf, dev->if_port);
 
-	/* Block a timer-based transmit from overlapping.  This could better be
-	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
-	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
-		printk("%s: Transmitter access conflict.\n", dev->name);
-	else {
-		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-		unsigned char *buf = skb->data;
-		int flags;
-
-		/* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
-		   This sequence must not be interrupted by an incoming packet. */
-		save_flags(flags);
-		cli();
-		write_reg(ioaddr, IMR, 0);
-		write_reg_high(ioaddr, IMR, 0);
-		restore_flags(flags);
-
-		write_packet(ioaddr, length, buf, dev->if_port);
-
-		lp->pac_cnt_in_tx_buf++;
-		if (lp->tx_unit_busy == 0) {
-			trigger_send(ioaddr, length);
-			lp->saved_tx_size = 0; 				/* Redundant */
-			lp->re_tx = 0;
+	lp->pac_cnt_in_tx_buf++;
+	if (lp->tx_unit_busy == 0) {
+		trigger_send(ioaddr, length);
+		lp->saved_tx_size = 0; 				/* Redundant */
+		lp->re_tx = 0;
 			lp->tx_unit_busy = 1;
-		} else
-			lp->saved_tx_size = length;
-
-		dev->trans_start = jiffies;
-		/* Re-enable the LPT interrupts. */
-		write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
-		write_reg_high(ioaddr, IMR, ISRh_RxErr);
-	}
+	} else
+		lp->saved_tx_size = length;
 
+	dev->trans_start = jiffies;
+	/* Re-enable the LPT interrupts. */
+	write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+	write_reg_high(ioaddr, IMR, ISRh_RxErr);
 	dev_kfree_skb (skb);
-
 	return 0;
 }
 
@@ -482,14 +476,10 @@
 	int ioaddr, status, boguscount = 20;
 	static int num_tx_since_rx = 0;
 
-	if (dev == NULL) {
-		printk ("ATP_interrupt(): irq %d for unknown device.\n", irq);
-		return;
-	}
-	dev->interrupt = 1;
-
 	ioaddr = dev->base_addr;
 	lp = (struct net_local *)dev->priv;
+	
+	spin_lock(&lp->lock);
 
 	/* Disable additional spurious interrupts. */
 	outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
@@ -498,10 +488,14 @@
 	write_reg(ioaddr, CMR2, CMR2_NULL);
 	write_reg(ioaddr, IMR, 0);
 
-	if (net_debug > 5) printk("%s: In interrupt ", dev->name);
-    while (--boguscount > 0) {
+	if (net_debug > 5)
+		printk("%s: In interrupt ", dev->name);
+
+	while (--boguscount > 0) 
+	{
 		status = read_nibble(ioaddr, ISR);
-		if (net_debug > 5) printk("loop status %02x..", status);
+		if (net_debug > 5)
+			printk("loop status %02x..", status);
 
 		if (status & (ISR_RxOK<<3)) {
 			write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
@@ -539,7 +533,8 @@
 					break;
 				}
 				/* Attempt to retransmit. */
-				if (net_debug > 6)  printk("attempting to ReTx");
+				if (net_debug > 6)
+					printk("attempting to ReTx");
 				write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
 			} else {
 				/* Finish up the transmit. */
@@ -551,8 +546,7 @@
 					lp->re_tx = 0;
 				} else
 					lp->tx_unit_busy = 0;
-				dev->tbusy = 0;
-				mark_bh(NET_BH);	/* Inform upper layers. */
+				netif_wake_queue(dev);	/* Inform upper layers. */
 			}
 			num_tx_since_rx++;
 		} else if (num_tx_since_rx > 8
@@ -568,7 +562,7 @@
 			break;
 		} else
 			break;
-    }
+	}
 
 	/* This following code fixes a rare (and very difficult to track down)
 	   problem where the adapter forgets its ethernet address. */
@@ -584,17 +578,17 @@
 	}
 
 	/* Tell the adapter that it can go back to using the output line as IRQ. */
-    write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+	write_reg(ioaddr, CMR2, CMR2_IRQOUT);
 	/* Enable the physical interrupt line, which is sure to be low until.. */
 	outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
 	/* .. we enable the interrupt sources. */
 	write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
 	write_reg_high(ioaddr, IMR, ISRh_RxErr); 			/* Hmmm, really needed? */
 
-	if (net_debug > 5) printk("exiting interrupt.\n");
-
-	dev->interrupt = 0;
-
+	if (net_debug > 5)
+		printk("exiting interrupt.\n");
+		
+	spin_unlock(&lp->lock);
 	return;
 }
 
@@ -603,33 +597,17 @@
    problem where the adapter forgets its ethernet address. */
 static void atp_timed_checker(unsigned long ignored)
 {
-  int i;
-  int ioaddr = atp_timed_dev->base_addr;
+	int i;
+	struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
+	int ioaddr = atp_timed_dev->base_addr;
 
-  if (!atp_timed_dev->interrupt)
-	{
-	  for (i = 0; i < 6; i++)
-#if 0
-		if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
-		  {
-			struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
-			write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
-			if (i == 2)
-			  lp->stats.tx_errors++;
-			else if (i == 3)
-			  lp->stats.tx_dropped++;
-			else if (i == 4)
-			  lp->stats.collisions++;
-			else
-			  lp->stats.rx_errors++;
-		  }
-#else
-	  write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
-#endif
-	}
-  del_timer(&atp_timer);
-  atp_timer.expires = jiffies + TIMED_CHECKER;
-  add_timer(&atp_timer);
+	spin_lock(&lp->lock);
+	for (i = 0; i < 6; i++)
+		write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+	spin_unlock(&lp->lock);
+	del_timer(&atp_timer);
+	atp_timer.expires = jiffies + TIMED_CHECKER;
+	add_timer(&atp_timer);
 }
 #endif
 
@@ -707,19 +685,17 @@
 	else
 		do      *p++ = read_byte_mode6(ioaddr);  while (--length > 0);
 
-    outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
+	outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
 	outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
 }
 
 /* The inverse routine to net_open(). */
-static int
-net_close(struct net_device *dev)
+static int net_close(struct net_device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 
-	dev->tbusy = 1;
-	dev->start = 0;
+	netif_stop_queue(dev);
 
 	/* Flush the Tx and disable Rx here. */
 	lp->addr_mode = CMR2h_OFF;
@@ -730,7 +706,7 @@
 	free_irq(dev->irq, dev);
 
 	/* Leave the hardware in a reset state. */
-    write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
 
 	return 0;
 }
@@ -774,3 +750,27 @@
  *  tab-width: 4
  * End:
  */
+
+#ifdef MODULE
+
+static int io = 0;
+static char nullname[8] = "";
+static struct net_device atp_dev = {
+	nullname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, atp_probe };
+	
+MODULE_PARM(io, "I/O port of the pocket adapter");
+
+int init_module(void)
+{
+	atp_dev.base_addr = io;
+	if (register_netdev(&atp_dev) != 0)
+		return -EIO;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unregister_netdev(&atp_dev);
+}
+
+#endif

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