[PATCH] Improved PLIP driver, take 2

Nimrod Zimerman (zimerman@mailandnews.com)
Fri, 27 Aug 1999 00:42:26 +0300


Hello.

This is a second attempt at an improved PLIP driver. It does the following:

* Allow using PLIP with parallel ports without IRQ.
* Move the PLIP driver to use the generic parallel port API, rather then
using direct I/O access.
* Use MAC addresses on PLIP ethernet packets all the time (this also fixes a
problem that was reported here a few weeks ago, of tcpdump not handling plip
correctly). I'm afraid that I don't fully understand what
dev->rebuild_header() is all about, and I couldn't get the old version of
the code to do anything useful, so I changed the way of doing things. Any
hints here would be appreciated, though I find the current solution quite
satisfactory, as it works and has no outstanding overhead.

The patch as follows below compiles and works with 2.2.10. It should work
with any other 2.2.x. It also compiles (and work) with 2.3.12, and probably
with other development kernels, with a slight change.

Please let me know if this needs any more changes. I can see no reason not
to include this in the kernel.

The patch can (also) be found at http://thor.prohosting.com/~zimerman.

Nimrod

--- ./linux/drivers/net/plip.c.ORIG-plip Tue Mar 9 11:47:18 1999
+++ ./linux/drivers/net/plip.c Thu Aug 26 23:58:41 1999
@@ -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:
@@ -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 device *dev);
static void plip_bh(struct device *dev);
+static void plip_timer_bh(struct 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 device *dev);
+static int plip_hard_header(struct sk_buff *skb, struct 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 device *dev);
static int plip_close(struct device *dev);
static struct net_device_stats *plip_get_stats(struct 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 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 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 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 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 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 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 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 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 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 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 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 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 device *dev)
{
@@ -955,6 +1013,51 @@
return 0;
}

+static void
+plip_rewrite_address(struct 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 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)
+ {
+ nl->killed_timer_sem = MUTEX_LOCKED;
+ 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;
--- ./linux/Documentation/networking/PLIP.txt.ORIG-plip Thu May 21 04:54:34 1998
+++ ./linux/Documentation/networking/PLIP.txt Wed Jul 28 15:17:55 1999
@@ -49,17 +49,69 @@
SLIP).

Performance
-==========
+===========

PLIP easily outperforms Ethernet cards....(ups, I was dreaming, but
it *is* getting late. EOB)

+PLIP driver details
+-------------------
+
+The Linux PLIP driver is an implementation of the original Crynwr protocol,
+that uses the parallel port subsystem of the kernel in order to properly
+share parallel ports between PLIP and other services.
+
+IRQs and trigger timeouts
+=========================
+
+When a parallel port used for a PLIP driver has an IRQ configured to it, the
+PLIP driver is signaled whenever data is sent to it via the cable, such that
+when no data is available, the driver isn't being used.
+
+However, on some machines it is hard, if not impossible, to configure an IRQ
+to a certain parallel port, mainly because it is used by some other device.
+On these machines, the PLIP driver can be used in IRQ-less mode, where
+the PLIP driver would constantly poll the parallel port for data waiting,
+and if such data is available, process it. This mode is less efficient than
+the IRQ mode, because the driver has to check the parallel port many times
+per second, even when no data at all is sent. Some rough measurements
+indicate that there isn't a noticeable performance drop when using IRQ-less
+mode as compared to IRQ mode as far as the data transfer speed is involved.
+There is a performance drop on the machine hosting the driver.
+
+When the PLIP driver is used in IRQ mode, the timeout used for triggering a
+data transfer (the maximal time the PLIP driver would allow the other side
+before announcing a timeout, when trying to handshake a transfer of some
+data) is, by default, 500usec. As IRQ delivery is more or less immediate,
+this timeout is quite sufficient.
+
+When in IRQ-less mode, the PLIP driver polls the parallel port HZ times
+per second (where HZ is typically 100 on most platforms, and 1024 on an
+Alpha, as of this writing). Between two such polls, there are 10^6/HZ usecs.
+On an i386, for example, 10^6/100 = 10000usec. It is easy to see that it is
+quite possible for the trigger timeout to expire between two such polls, as
+the timeout is only 500usec long. As a result, it is required to change the
+trigger timeout on the *other* side of a PLIP connection, to about
+10^6/HZ usecs. If both sides of a PLIP connection are used in IRQ-less mode,
+this timeout is required on both sides.
+
+It appears that in practice, the trigger timeout can be shorter than in the
+above calculation. It isn't an important issue, unless the wire is faulty,
+in which case a long timeout would stall the machine when, for whatever
+reason, bits are dropped.
+
+A utility that can perform this change in Linux is plipconfig, which is part
+of the net-tools package (its location can be found in the
+Documentation/Changes file). An example command would be
+'plipconfig plipX trigger 10000', where plipX is the appropriate
+PLIP device.
+
PLIP hardware interconnection
-----------------------------

PLIP uses several different data transfer methods. The first (and the
only one implemented in the early version of the code) uses a standard
-printer "null" cable to transfers data four bits at a time using
+printer "null" cable to transfer data four bits at a time using
data bit outputs connected to status bit inputs.

The second data transfer method relies on both machines having
@@ -138,18 +190,18 @@
The PLIP driver is compatible with the "Crynwr" parallel port transfer
standard in Mode 0. That standard specifies the following protocol:

- send header nibble '8'
+ send header nibble '0x08'
count-low octet
count-high octet
... data octets
checksum octet

Each octet is sent as
- <wait for rx. '1'> <send 0x10+(octet&0x0F)>
- <wait for rx. '0'> <send 0x00+((octet>>4)&0x0F)>
+ <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
+ <wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>

To start a transfer the transmitting machine outputs a nibble 0x08.
-The raises the ACK line, triggering an interrupt in the receiving
+That raises the ACK line, triggering an interrupt in the receiving
machine. The receiving machine disables interrupts and raises its own ACK
line.

--- ./linux/Documentation/Configure.help.ORIG-plip Mon Jun 14 22:54:12 1999
+++ ./linux/Documentation/Configure.help Wed Jul 21 19:54:12 1999
@@ -5045,6 +5045,23 @@
The kernels on both machines need to have this PLIP option enabled
for this to work.

+ The plipconfig utility, can be used to change several parameters
+ of the plip driver once it has been opened. It is part of the net-tools
+ package, whose location and current version number is contained in
+ the file Documentation/Changes.
+
+ This drivers performs better if the parallel port used has an IRQ
+ associated with it, but can also work in poll mode with no IRQ.
+ If IRQ-less mode is used, it is required to change the trigger timeout
+ on the *other* side of the connection to about 10000usec (on a i386),
+ though values as small as 5000usec are known to work. If both sides are
+ used in IRQ-less mode, both sides need to use said trigger timeout.
+ In order to perform this change on a Linux, the command
+ 'plipconfig plipX trigger 10000' can be used, where plipX is the
+ appropriate PLIP device.
+ For a discussion on the trigger timeout, see
+ Documentation/networking/PLIP.txt.
+
The PLIP driver has two modes, mode 0 and mode 1. The parallel ports
(the connectors at the computers with 25 holes) are connected with
"null printer" or "Turbo Laplink" cables which can transmit 4 bits

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/