patch-2.3.17 linux/drivers/net/plip.c

Next file: linux/drivers/net/ppp_async.c
Previous file: linux/drivers/net/irda/toshoboe.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.16/linux/drivers/net/plip.c linux/drivers/net/plip.c
@@ -2,17 +2,22 @@
 /* PLIP: A parallel port "network" driver for Linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
- * Authors:	Donald Becker,  <becker@super.org>
- *		Tommy Thorn, <thorn@daimi.aau.dk>
- *		Tanabe Hiroyasu, <hiro@sanpo.t.u-tokyo.ac.jp>
- *		Alan Cox, <gw4pts@gw4pts.ampr.org>
- *		Peter Bauer, <100136.3530@compuserve.com>
- *		Niibe Yutaka, <gniibe@mri.co.jp>
+ * Authors:	Donald Becker <becker@super.org>
+ *		Tommy Thorn <thorn@daimi.aau.dk>
+ *		Tanabe Hiroyasu <hiro@sanpo.t.u-tokyo.ac.jp>
+ *		Alan Cox <gw4pts@gw4pts.ampr.org>
+ *		Peter Bauer <100136.3530@compuserve.com>
+ *		Niibe Yutaka <gniibe@mri.co.jp>
+ *		Nimrod Zimerman <zimerman@mailandnews.com>
  *
+ * Enhancements:
  *		Modularization and ifreq/ifmap support by Alan Cox.
  *		Rewritten by Niibe Yutaka.
- *              parport-sharing awareness code by Philip Blundell.
+ *		parport-sharing awareness code by Philip Blundell.
  *		SMP locking by Niibe Yutaka.
+ *		Support for parallel ports with no IRQ (poll mode),
+ *		Modifications to use the parallel port API 
+ *		by Nimrod Zimerman.
  *
  * Fixes:
  *		Niibe Yutaka
@@ -49,7 +54,7 @@
  *     To use with DOS box, please do (Turn on ARP switch):
  *	# ifconfig plip[0-2] arp
  */
-static const char *version = "NET3 PLIP version 2.3-parport gniibe@mri.co.jp\n";
+static const char *version = "NET3 PLIP version 2.4-parport gniibe@mri.co.jp\n";
 
 /*
   Sources:
@@ -57,7 +62,7 @@
 	"parallel.asm" parallel port packet driver.
 
   The "Crynwr" parallel port standard specifies the following protocol:
-    Trigger by sending '0x08' (this cause interrupt on other end)
+    Trigger by sending nibble '0x8' (this causes interrupt on other end)
     count-low octet
     count-high octet
     ... data octets
@@ -93,7 +98,6 @@
 #include <linux/ptrace.h>
 #include <linux/if_ether.h>
 #include <asm/system.h>
-#include <asm/io.h>
 #include <linux/in.h>
 #include <linux/errno.h>
 #include <linux/delay.h>
@@ -105,6 +109,7 @@
 #include <linux/inetdevice.h>
 #include <linux/skbuff.h>
 #include <linux/if_plip.h>
+#include <net/neighbour.h>
 
 #include <linux/tqueue.h>
 #include <linux/ioport.h>
@@ -112,6 +117,7 @@
 #include <asm/irq.h>
 #include <asm/byteorder.h>
 #include <asm/spinlock.h>
+#include <asm/semaphore.h>
 
 #include <linux/parport.h>
 
@@ -124,8 +130,8 @@
 #endif
 static unsigned int net_debug = NET_DEBUG;
 
-#define ENABLE(irq) enable_irq(irq)
-#define DISABLE(irq) disable_irq(irq)
+#define ENABLE(irq)  if (irq != -1) enable_irq(irq)
+#define DISABLE(irq) if (irq != -1) disable_irq(irq)
 
 /* In micro second */
 #define PLIP_DELAY_UNIT		   1
@@ -136,22 +142,21 @@
 /* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
 #define PLIP_NIBBLE_WAIT        3000
 
-#define PAR_INTR_ON		(LP_PINITP|LP_PSELECP|LP_PINTEN)
-#define PAR_INTR_OFF		(LP_PINITP|LP_PSELECP)
-#define PAR_DATA(dev)		((dev)->base_addr+0)
-#define PAR_STATUS(dev)		((dev)->base_addr+1)
-#define PAR_CONTROL(dev)	((dev)->base_addr+2)
-
-/* Bottom halfs */
+/* Bottom halves */
 static void plip_kick_bh(struct net_device *dev);
 static void plip_bh(struct net_device *dev);
+static void plip_timer_bh(struct net_device *dev);
 
 /* Interrupt handler */
 static void plip_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
 /* Functions for DEV methods */
-static int plip_rebuild_header(struct sk_buff *skb);
 static int plip_tx_packet(struct sk_buff *skb, struct net_device *dev);
+static int plip_hard_header(struct sk_buff *skb, struct net_device *dev,
+                            unsigned short type, void *daddr,
+                            void *saddr, unsigned len);
+static int plip_hard_header_cache(struct neighbour *neigh,
+                                  struct hh_cache *hh);
 static int plip_open(struct net_device *dev);
 static int plip_close(struct net_device *dev);
 static struct net_device_stats *plip_get_stats(struct net_device *dev);
@@ -210,6 +215,7 @@
 	struct net_device_stats enet_stats;
 	struct tq_struct immediate;
 	struct tq_struct deferred;
+	struct tq_struct timer;
 	struct plip_local snd_data;
 	struct plip_local rcv_data;
 	struct pardevice *pardev;
@@ -220,10 +226,52 @@
 	int is_deferred;
 	int port_owner;
 	int should_relinquish;
-	int (*orig_rebuild_header)(struct sk_buff *skb);
+	int (*orig_hard_header)(struct sk_buff *skb, struct net_device *dev,
+	                        unsigned short type, void *daddr,
+	                        void *saddr, unsigned len);
+	int (*orig_hard_header_cache)(struct neighbour *neigh,
+	                              struct hh_cache *hh);
 	spinlock_t lock;
+	atomic_t kill_timer;
+	struct semaphore killed_timer_sem;
 };
 
+inline static void enable_parport_interrupts (struct net_device *dev)
+{
+	if (dev->irq != -1)
+	{
+		struct parport *port =
+		   ((struct net_local *)dev->priv)->pardev->port;
+		port->ops->enable_irq (port);
+	}
+}
+
+inline static void disable_parport_interrupts (struct net_device *dev)
+{
+	if (dev->irq != -1)
+	{
+		struct parport *port =
+		   ((struct net_local *)dev->priv)->pardev->port;
+		port->ops->disable_irq (port);
+	}
+}
+
+inline static void write_data (struct net_device *dev, unsigned char data)
+{
+	struct parport *port =
+	   ((struct net_local *)dev->priv)->pardev->port;
+
+	port->ops->write_data (port, data);
+}
+
+inline static unsigned char read_status (struct net_device *dev)
+{
+	struct parport *port =
+	   ((struct net_local *)dev->priv)->pardev->port;
+
+	return port->ops->read_status (port);
+}
+
 /* Entry point of PLIP driver.
    Probe the hardware, and register/initialize the driver.
 
@@ -243,8 +291,8 @@
 	dev->base_addr = pb->base;
 
 	if (pb->irq == -1) {
-		printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name);
-		return -ENODEV;
+		printk(KERN_INFO "plip: %s has no IRQ. Using IRQ-less mode,"
+		                 "which is fairly inefficient!\n", pb->name);
 	}
 
 	pardev = parport_register_device(pb, dev->name, plip_preempt,
@@ -255,21 +303,26 @@
 		return -ENODEV;
 
 	printk(KERN_INFO "%s", version);
-	printk(KERN_INFO "%s: Parallel port at %#3lx, using IRQ %d\n", dev->name,
-	       dev->base_addr, dev->irq);
+	if (dev->irq != -1)
+		printk(KERN_INFO "%s: Parallel port at %#3lx, using IRQ %d.\n",
+		       dev->name, dev->base_addr, dev->irq);
+	else
+		printk(KERN_INFO "%s: Parallel port at %#3lx, not using IRQ.\n",
+		       dev->name, dev->base_addr);
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
 
 	/* Then, override parts of it */
-	dev->hard_start_xmit	= plip_tx_packet;
-	dev->open		= plip_open;
-	dev->stop		= plip_close;
-	dev->get_stats 		= plip_get_stats;
-	dev->set_config		= plip_config;
-	dev->do_ioctl		= plip_ioctl;
-	dev->tx_queue_len	= 10;
-	dev->flags	        = IFF_POINTOPOINT|IFF_NOARP;
+	dev->hard_start_xmit	 = plip_tx_packet;
+	dev->open		 = plip_open;
+	dev->stop		 = plip_close;
+	dev->get_stats 		 = plip_get_stats;
+	dev->set_config		 = plip_config;
+	dev->do_ioctl		 = plip_ioctl;
+	dev->header_cache_update = NULL;
+	dev->tx_queue_len 	 = 10;
+	dev->flags	         = IFF_POINTOPOINT|IFF_NOARP;
 	memset(dev->dev_addr, 0xfc, ETH_ALEN);
 
 	/* Set the private structure */
@@ -282,8 +335,12 @@
 	memset(dev->priv, 0, sizeof(struct net_local));
 	nl = (struct net_local *) dev->priv;
 
-	nl->orig_rebuild_header = dev->rebuild_header;
-	dev->rebuild_header 	= plip_rebuild_header;
+	nl->orig_hard_header    = dev->hard_header;
+	dev->hard_header        = plip_hard_header;
+
+	nl->orig_hard_header_cache = dev->hard_header_cache;
+	dev->hard_header_cache     = plip_hard_header_cache;
+
 	nl->pardev = pardev; 
 
 	nl->port_owner = 0;
@@ -295,13 +352,21 @@
 	/* Initialize task queue structures */
 	nl->immediate.next = NULL;
 	nl->immediate.sync = 0;
-	nl->immediate.routine = (void *)(void *)plip_bh;
+	nl->immediate.routine = (void (*)(void *))plip_bh;
 	nl->immediate.data = dev;
 
 	nl->deferred.next = NULL;
 	nl->deferred.sync = 0;
-	nl->deferred.routine = (void *)(void *)plip_kick_bh;
+	nl->deferred.routine = (void (*)(void *))plip_kick_bh;
 	nl->deferred.data = dev;
+
+	if (dev->irq == -1) {
+		nl->timer.next = NULL;
+		nl->timer.sync = 0;
+		nl->timer.routine = (void (*)(void *))plip_timer_bh;
+		nl->timer.data = dev;
+	}
+
 	spin_lock_init(&nl->lock);
 
 	return 0;
@@ -373,6 +438,22 @@
 	}
 }
 
+static void
+plip_timer_bh(struct net_device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	
+	if (!(atomic_read (&nl->kill_timer))) {
+		if (!dev->interrupt)
+			plip_interrupt (-1, dev, NULL);
+
+		queue_task (&nl->timer, &tq_timer);
+	}
+	else {
+		up (&nl->killed_timer_sem);
+	}
+}
+
 static int
 plip_bh_timeout_error(struct net_device *dev, struct net_local *nl,
 		      struct plip_local *snd, struct plip_local *rcv,
@@ -401,7 +482,7 @@
 				/* Try again later */
 				return TIMEOUT;
 			}
-			c0 = inb(PAR_STATUS(dev));
+			c0 = read_status(dev);
 			printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n",
 			       dev->name, snd->state, c0);
 		} else
@@ -420,7 +501,7 @@
 				/* Try again later */
 				return TIMEOUT;
 			}
-			c0 = inb(PAR_STATUS(dev));
+			c0 = read_status(dev);
 			printk(KERN_WARNING "%s: receive timeout(%d,%02x)\n",
 			       dev->name, rcv->state, c0);
 		}
@@ -441,10 +522,10 @@
 		DISABLE(dev->irq);
 		synchronize_irq();
 	}
-	outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+	disable_parport_interrupts (dev);
 	dev->tbusy = 1;
 	nl->connection = PLIP_CN_ERROR;
-	outb(0x00, PAR_DATA(dev));
+	write_data (dev, 0x00);
 
 	return TIMEOUT;
 }
@@ -459,7 +540,7 @@
 /* PLIP_RECEIVE --- receive a byte(two nibbles)
    Returns OK on success, TIMEOUT on timeout */
 inline static int
-plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
+plip_receive(unsigned short nibble_timeout, struct net_device *dev,
 	     enum plip_nibble_state *ns_p, unsigned char *data_p)
 {
 	unsigned char c0, c1;
@@ -469,10 +550,10 @@
 	case PLIP_NB_BEGIN:
 		cx = nibble_timeout;
 		while (1) {
-			c0 = inb(status_addr);
+			c0 = read_status(dev);
 			udelay(PLIP_DELAY_UNIT);
 			if ((c0 & 0x80) == 0) {
-				c1 = inb(status_addr);
+				c1 = read_status(dev);
 				if (c0 == c1)
 					break;
 			}
@@ -480,17 +561,16 @@
 				return TIMEOUT;
 		}
 		*data_p = (c0 >> 3) & 0x0f;
-		outb(0x10, --status_addr); /* send ACK */
-		status_addr++;
+		write_data (dev, 0x10); /* send ACK */
 		*ns_p = PLIP_NB_1;
 
 	case PLIP_NB_1:
 		cx = nibble_timeout;
 		while (1) {
-			c0 = inb(status_addr);
+			c0 = read_status(dev);
 			udelay(PLIP_DELAY_UNIT);
 			if (c0 & 0x80) {
-				c1 = inb(status_addr);
+				c1 = read_status(dev);
 				if (c0 == c1)
 					break;
 			}
@@ -498,8 +578,7 @@
 				return TIMEOUT;
 		}
 		*data_p |= (c0 << 1) & 0xf0;
-		outb(0x00, --status_addr); /* send ACK */
-		status_addr++;
+		write_data (dev, 0x00); /* send ACK */
 		*ns_p = PLIP_NB_BEGIN;
 	case PLIP_NB_2:
 		break;
@@ -512,7 +591,6 @@
 plip_receive_packet(struct net_device *dev, struct net_local *nl,
 		    struct plip_local *snd, struct plip_local *rcv)
 {
-	unsigned short status_addr = PAR_STATUS(dev);
 	unsigned short nibble_timeout = nl->nibble;
 	unsigned char *lbuf;
 
@@ -520,9 +598,9 @@
 	case PLIP_PK_TRIGGER:
 		DISABLE(dev->irq);
 		/* Don't need to synchronize irq, as we can safely ignore it */
-		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+		disable_parport_interrupts (dev);
 		dev->interrupt = 0;
-		outb(0x01, PAR_DATA(dev)); /* send ACK */
+		write_data (dev, 0x01); /* send ACK */
 		if (net_debug > 2)
 			printk(KERN_DEBUG "%s: receive start\n", dev->name);
 		rcv->state = PLIP_PK_LENGTH_LSB;
@@ -530,26 +608,26 @@
 
 	case PLIP_PK_LENGTH_LSB:
 		if (snd->state != PLIP_PK_DONE) {
-			if (plip_receive(nl->trigger, status_addr,
+			if (plip_receive(nl->trigger, dev,
 					 &rcv->nibble, &rcv->length.b.lsb)) {
 				/* collision, here dev->tbusy == 1 */
 				rcv->state = PLIP_PK_DONE;
 				nl->is_deferred = 1;
 				nl->connection = PLIP_CN_SEND;
 				queue_task(&nl->deferred, &tq_timer);
-				outb(PAR_INTR_ON, PAR_CONTROL(dev));
+				enable_parport_interrupts (dev);
 				ENABLE(dev->irq);
 				return OK;
 			}
 		} else {
-			if (plip_receive(nibble_timeout, status_addr,
+			if (plip_receive(nibble_timeout, dev,
 					 &rcv->nibble, &rcv->length.b.lsb))
 				return TIMEOUT;
 		}
 		rcv->state = PLIP_PK_LENGTH_MSB;
 
 	case PLIP_PK_LENGTH_MSB:
-		if (plip_receive(nibble_timeout, status_addr,
+		if (plip_receive(nibble_timeout, dev,
 				 &rcv->nibble, &rcv->length.b.msb))
 			return TIMEOUT;
 		if (rcv->length.h > dev->mtu + dev->hard_header_len
@@ -572,7 +650,7 @@
 	case PLIP_PK_DATA:
 		lbuf = rcv->skb->data;
 		do
-			if (plip_receive(nibble_timeout, status_addr,
+			if (plip_receive(nibble_timeout, dev,
 					 &rcv->nibble, &lbuf[rcv->byte]))
 				return TIMEOUT;
 		while (++rcv->byte < rcv->length.h);
@@ -582,7 +660,7 @@
 		rcv->state = PLIP_PK_CHECKSUM;
 
 	case PLIP_PK_CHECKSUM:
-		if (plip_receive(nibble_timeout, status_addr,
+		if (plip_receive(nibble_timeout, dev,
 				 &rcv->nibble, &rcv->data))
 			return TIMEOUT;
 		if (rcv->data != rcv->checksum) {
@@ -604,20 +682,20 @@
 			printk(KERN_DEBUG "%s: receive end\n", dev->name);
 
 		/* Close the connection. */
-		outb (0x00, PAR_DATA(dev));
+		write_data (dev, 0x00);
 		spin_lock_irq(&nl->lock);
 		if (snd->state != PLIP_PK_DONE) {
 			nl->connection = PLIP_CN_SEND;
 			spin_unlock_irq(&nl->lock);
 			queue_task(&nl->immediate, &tq_immediate);
 			mark_bh(IMMEDIATE_BH);
-			outb(PAR_INTR_ON, PAR_CONTROL(dev));
+			enable_parport_interrupts (dev);
 			ENABLE(dev->irq);
 			return OK;
 		} else {
 			nl->connection = PLIP_CN_NONE;
 			spin_unlock_irq(&nl->lock);
-			outb(PAR_INTR_ON, PAR_CONTROL(dev));
+			enable_parport_interrupts (dev);
 			ENABLE(dev->irq);
 			return OK;
 		}
@@ -628,7 +706,7 @@
 /* PLIP_SEND --- send a byte (two nibbles)
    Returns OK on success, TIMEOUT when timeout    */
 inline static int
-plip_send(unsigned short nibble_timeout, unsigned short data_addr,
+plip_send(unsigned short nibble_timeout, struct net_device *dev,
 	  enum plip_nibble_state *ns_p, unsigned char data)
 {
 	unsigned char c0;
@@ -636,37 +714,34 @@
 
 	switch (*ns_p) {
 	case PLIP_NB_BEGIN:
-		outb((data & 0x0f), data_addr);
+		write_data (dev, data & 0x0f);
 		*ns_p = PLIP_NB_1;
 
 	case PLIP_NB_1:
-		outb(0x10 | (data & 0x0f), data_addr);
+		write_data (dev, 0x10 | (data & 0x0f));
 		cx = nibble_timeout;
-		data_addr++;
 		while (1) {
-			c0 = inb(data_addr);
+			c0 = read_status(dev);
 			if ((c0 & 0x80) == 0)
 				break;
 			if (--cx == 0)
 				return TIMEOUT;
 			udelay(PLIP_DELAY_UNIT);
 		}
-		outb(0x10 | (data >> 4), --data_addr);
+		write_data (dev, 0x10 | (data >> 4));
 		*ns_p = PLIP_NB_2;
 
 	case PLIP_NB_2:
-		outb((data >> 4), data_addr);
-		data_addr++;
+		write_data (dev, (data >> 4));
 		cx = nibble_timeout;
 		while (1) {
-			c0 = inb(data_addr);
+			c0 = read_status(dev);
 			if (c0 & 0x80)
 				break;
 			if (--cx == 0)
 				return TIMEOUT;
 			udelay(PLIP_DELAY_UNIT);
 		}
-		data_addr--;
 		*ns_p = PLIP_NB_BEGIN;
 		return OK;
 	}
@@ -678,7 +753,6 @@
 plip_send_packet(struct net_device *dev, struct net_local *nl,
 		 struct plip_local *snd, struct plip_local *rcv)
 {
-	unsigned short data_addr = PAR_DATA(dev);
 	unsigned short nibble_timeout = nl->nibble;
 	unsigned char *lbuf;
 	unsigned char c0;
@@ -693,11 +767,11 @@
 
 	switch (snd->state) {
 	case PLIP_PK_TRIGGER:
-		if ((inb(PAR_STATUS(dev)) & 0xf8) != 0x80)
+		if ((read_status(dev) & 0xf8) != 0x80)
 			return HS_TIMEOUT;
 
 		/* Trigger remote rx interrupt. */
-		outb(0x08, data_addr);
+		write_data (dev, 0x08);
 		cx = nl->trigger;
 		while (1) {
 			udelay(PLIP_DELAY_UNIT);
@@ -708,7 +782,7 @@
 				nl->enet_stats.collisions++;
 				return OK;
 			}
-			c0 = inb(PAR_STATUS(dev));
+			c0 = read_status(dev);
 			if (c0 & 0x08) {
 				spin_unlock_irq(&nl->lock);
 				DISABLE(dev->irq);
@@ -724,7 +798,7 @@
 					nl->enet_stats.collisions++;
 					return OK;
 				}
-				outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+				disable_parport_interrupts (dev);
 				if (net_debug > 2)
 					printk(KERN_DEBUG "%s: send start\n", dev->name);
 				snd->state = PLIP_PK_LENGTH_LSB;
@@ -734,19 +808,19 @@
 			}
 			spin_unlock_irq(&nl->lock);
 			if (--cx == 0) {
-				outb(0x00, data_addr);
+				write_data (dev, 0x00);
 				return HS_TIMEOUT;
 			}
 		}
 
 	case PLIP_PK_LENGTH_LSB:
-		if (plip_send(nibble_timeout, data_addr,
+		if (plip_send(nibble_timeout, dev,
 			      &snd->nibble, snd->length.b.lsb))
 			return TIMEOUT;
 		snd->state = PLIP_PK_LENGTH_MSB;
 
 	case PLIP_PK_LENGTH_MSB:
-		if (plip_send(nibble_timeout, data_addr,
+		if (plip_send(nibble_timeout, dev,
 			      &snd->nibble, snd->length.b.msb))
 			return TIMEOUT;
 		snd->state = PLIP_PK_DATA;
@@ -755,7 +829,7 @@
 
 	case PLIP_PK_DATA:
 		do
-			if (plip_send(nibble_timeout, data_addr,
+			if (plip_send(nibble_timeout, dev,
 				      &snd->nibble, lbuf[snd->byte]))
 				return TIMEOUT;
 		while (++snd->byte < snd->length.h);
@@ -765,7 +839,7 @@
 		snd->state = PLIP_PK_CHECKSUM;
 
 	case PLIP_PK_CHECKSUM:
-		if (plip_send(nibble_timeout, data_addr,
+		if (plip_send(nibble_timeout, dev,
 			      &snd->nibble, snd->checksum))
 			return TIMEOUT;
 
@@ -776,14 +850,14 @@
 
 	case PLIP_PK_DONE:
 		/* Close the connection */
-		outb (0x00, data_addr);
+		write_data (dev, 0x00);
 		snd->skb = NULL;
 		if (net_debug > 2)
 			printk(KERN_DEBUG "%s: send end\n", dev->name);
 		nl->connection = PLIP_CN_CLOSING;
 		nl->is_deferred = 1;
 		queue_task(&nl->deferred, &tq_timer);
-		outb(PAR_INTR_ON, PAR_CONTROL(dev));
+		enable_parport_interrupts (dev);
 		ENABLE(dev->irq);
 		return OK;
 	}
@@ -815,7 +889,7 @@
 {
 	unsigned char status;
 
-	status = inb(PAR_STATUS(dev));
+	status = read_status(dev);
 	if ((status & 0xf8) == 0x80) {
 		if (net_debug > 2)
 			printk(KERN_DEBUG "%s: reset interface.\n", dev->name);
@@ -823,7 +897,7 @@
 		nl->should_relinquish = 0;
 		dev->tbusy = 0;
 		dev->interrupt = 0;
-		outb(PAR_INTR_ON, PAR_CONTROL(dev));
+		enable_parport_interrupts (dev);
 		ENABLE(dev->irq);
 		mark_bh(NET_BH);
 	} else {
@@ -854,9 +928,9 @@
 	if (dev->interrupt)
 		return;
 
-	c0 = inb(PAR_STATUS(dev));
+	c0 = read_status(dev);
 	if ((c0 & 0xf8) != 0xc0) {
-		if (net_debug > 1)
+		if ((dev->irq != -1) && (net_debug > 1))
 			printk(KERN_DEBUG "%s: spurious interrupt\n", dev->name);
 		return;
 	}
@@ -893,22 +967,6 @@
 	}
 }
 
-/* We don't need to send arp, for plip is point-to-point. */
-static int
-plip_rebuild_header(struct sk_buff *skb)
-{
-	struct net_device *dev = skb->dev;
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct ethhdr *eth = (struct ethhdr *)skb->data;
-
-	if ((dev->flags & IFF_NOARP)==0)
-		return nl->orig_rebuild_header(skb);
-
-	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-	memcpy(eth->h_dest, dev->broadcast, dev->addr_len);
-	return 0;
-}
-
 static int
 plip_tx_packet(struct sk_buff *skb, struct net_device *dev)
 {
@@ -955,6 +1013,51 @@
 	return 0;
 }
 
+static void
+plip_rewrite_address(struct net_device *dev, struct ethhdr *eth)
+{
+	struct in_device *in_dev;
+
+	if ((in_dev=dev->ip_ptr) != NULL) {
+		/* Any address will do - we take the first */
+		struct in_ifaddr *ifa=in_dev->ifa_list;
+		if (ifa != NULL) {
+			memcpy(eth->h_source, dev->dev_addr, 6);
+			memset(eth->h_dest, 0xfc, 2);
+			memcpy(eth->h_dest+2, &ifa->ifa_address, 4);
+		}
+	}
+}
+
+static int
+plip_hard_header(struct sk_buff *skb, struct net_device *dev,
+                 unsigned short type, void *daddr,
+	         void *saddr, unsigned len)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	int ret;
+
+	if ((ret = nl->orig_hard_header(skb, dev, type, daddr, saddr, len)) >= 0)
+		plip_rewrite_address (dev, (struct ethhdr *)skb->data);
+
+	return ret;
+}
+
+int plip_hard_header_cache(struct neighbour *neigh,
+                           struct hh_cache *hh)
+{
+	struct net_local *nl = (struct net_local *)neigh->dev->priv;
+	int ret;
+	
+	if ((ret = nl->orig_hard_header_cache(neigh, hh)) == 0)
+	{
+		struct ethhdr *eth = (struct ethhdr*)(((u8*)hh->hh_data) + 2);
+		plip_rewrite_address (neigh->dev, eth);
+	}
+	
+	return ret;
+}                          
+
 /* Open/initialize the board.  This is called (in the current kernel)
    sometime after booting when the 'ifconfig' program is run.
 
@@ -976,10 +1079,15 @@
 	nl->should_relinquish = 0;
 
 	/* Clear the data port. */
-	outb (0x00, PAR_DATA(dev));
+	write_data (dev, 0x00);
 
 	/* Enable rx interrupt. */
-	outb(PAR_INTR_ON, PAR_CONTROL(dev));
+	enable_parport_interrupts (dev);
+	if (dev->irq == -1)
+	{
+		atomic_set (&nl->kill_timer, 0);
+		queue_task (&nl->timer, &tq_timer);
+	}
 
 	/* Initialize the state machine. */
 	nl->rcv_data.state = nl->snd_data.state = PLIP_PK_DONE;
@@ -988,21 +1096,24 @@
 	nl->is_deferred = 0;
 
 	/* Fill in the MAC-level header.
-	   (ab)Use "dev->broadcast" to store point-to-point MAC address.
-
-	   PLIP doesn't have a real mac address, but we need to create one
-	   to be DOS compatible.  */
-	memset(dev->dev_addr,  0xfc, ETH_ALEN);
-	memset(dev->broadcast, 0xfc, ETH_ALEN);
+	   We used to abuse dev->broadcast to store the point-to-point
+	   MAC address, but we no longer do it. Instead, we fetch the
+	   interface address whenever it is needed, which is cheap enough
+	   because we use the hh_cache. Actually, abusing dev->broadcast
+	   didn't work, because when using plip_open the point-to-point
+	   address isn't yet known.
+	   PLIP doesn't have a real MAC address, but we need it to be
+	   DOS compatible, and to properly support taps (otherwise,
+	   when the device address isn't identical to the address of a
+	   received frame, the kernel incorrectly drops it).             */
 
 	if ((in_dev=dev->ip_ptr) != NULL) {
-		/*
-		 *	Any address will do - we take the first
-		 */
+		/* Any address will do - we take the first. We already
+		   have the first two bytes filled with 0xfc, from
+		   plip_init_dev(). */
 		struct in_ifaddr *ifa=in_dev->ifa_list;
 		if (ifa != NULL) {
 			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
-			memcpy(dev->broadcast+2, &ifa->ifa_address, 4);
 		}
 	}
 
@@ -1027,6 +1138,13 @@
 	DISABLE(dev->irq);
 	synchronize_irq();
 
+	if (dev->irq == -1)
+	{
+		init_MUTEX_LOCKED (&nl->killed_timer_sem);
+		atomic_set (&nl->kill_timer, 1);
+		down (&nl->killed_timer_sem);
+	}
+
 #ifdef NOTDEF
 	outb(0x00, PAR_DATA(dev));
 #endif
@@ -1095,7 +1213,7 @@
 	if (!parport_claim(nl->pardev)) {
 		nl->port_owner = 1;
 		/* Clear the data port. */
-		outb (0x00, PAR_DATA(dev));
+		write_data (dev, 0x00);
 	}
 
 	return;

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