summaryrefslogtreecommitdiffstats
path: root/drivers/net/plip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/plip.c')
-rw-r--r--drivers/net/plip.c1568
1 files changed, 845 insertions, 723 deletions
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index e0c6a10b1..cc68fe356 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -1,4 +1,5 @@
-/* plip.c: A parallel port "network" driver for linux. */
+/* $Id: plip.c,v 1.12 1995/02/11 10:26:05 gniibe Exp $ */
+/* 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>
@@ -8,12 +9,8 @@
* Peter Bauer, <100136.3530@compuserve.com>
* Niibe Yutaka, <gniibe@mri.co.jp>
*
- * This is the all improved state based PLIP that Niibe Yutaka has contributed.
- *
- * Modularization by Alan Cox. I also added the plipconfig program to tune the timeouts
- * and ifmap support for funny serial port settings or setting odd values using the
- * modular plip. I also took the panic() calls out. I don't like panic - especially when
- * it can be avoided.
+ * Modularization and ifreq/ifmap support by Alan Cox.
+ * Rewritten by Niibe Yutaka.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,16 +21,18 @@
/*
* Original version and the name 'PLIP' from Donald Becker <becker@super.org>
* inspired by Russ Nelson's parallel port packet driver.
+ *
+ * NOTE:
+ * Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0.
+ * Because of the necessity to communicate to DOS machines with the
+ * Crynwr packet driver, Peter Bauer changed the protocol again
+ * back to original protocol.
+ *
+ * This version follows original PLIP protocol.
+ * So, this PLIP can't communicate the PLIP of Linux v1.0.
*/
-static char *version =
- "NET3 "
-#ifdef MODULE
- "MODULAR "
-#endif
- "PLIP.014 gniibe@mri.co.jp\n";
-
-#include <linux/config.h>
+static char *version = "NET3 PLIP version 2.0 gniibe@mri.co.jp\n";
/*
Sources:
@@ -41,17 +40,19 @@ static char *version =
"parallel.asm" parallel port packet driver.
The "Crynwr" parallel port standard specifies the following protocol:
- send header nibble '8'
- count-low octet
- count-high octet
- ... data octets
- checksum octet
+ Trigger by sending '0x08' (this cause interrupt on other end)
+ count-low octet
+ count-high octet
+ ... data octets
+ checksum octet
Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
<wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>
-The cable used is a de facto standard parallel null cable -- sold as
-a "LapLink" cable by various places. You'll need a 10-conductor cable to
-make one yourself. The wiring is:
+ The packet is encapsulated as if it were ethernet.
+
+ The cable used is a de facto standard parallel null cable -- sold as
+ a "LapLink" cable by various places. You'll need a 12-conductor cable to
+ make one yourself. The wiring is:
SLCTIN 17 - 17
GROUND 25 - 25
D0->ERROR 2 - 15 15 - 2
@@ -65,6 +66,14 @@ make one yourself. The wiring is:
extra grounds are 18,19,20,21,22,23,24
*/
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -89,15 +98,11 @@ make one yourself. The wiring is:
#include <linux/ioport.h>
#include <asm/bitops.h>
#include <asm/irq.h>
+#include <asm/byteorder.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
-/* use 0 for production, 1 for verification, >2 for debug */
+/* Use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
-#define NET_DEBUG 3
+#define NET_DEBUG 1
#endif
static unsigned int net_debug = NET_DEBUG;
@@ -108,798 +113,923 @@ static unsigned int net_debug = NET_DEBUG;
#define PLIP_TRIGGER_WAIT 500
/* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
-#define PLIP_NIBBLE_WAIT 5000
+#define PLIP_NIBBLE_WAIT 3000
-#define PAR_DATA(dev) (dev->base_addr+0)
-#define PAR_STATUS(dev) (dev->base_addr+1)
-#define PAR_CONTROL(dev) (dev->base_addr+2)
+#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)
-/* Index to functions, as function prototypes. */
+/* Bottom halfs */
+static void plip_kick_bh(struct device *dev);
+static void plip_bh(struct device *dev);
+
+/* Interrupt handler */
+static void plip_interrupt(int irq, struct pt_regs *regs);
+
+/* Functions for DEV methods */
+static int plip_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
static int plip_open(struct device *dev);
static int plip_close(struct device *dev);
static struct enet_statistics *plip_get_stats(struct device *dev);
-static int plip_rebuild_header(void *buff, struct device *dev,
- unsigned long raddr, struct sk_buff *skb);
-static void plip_kick_bh(struct device *dev);
-static void plip_bh(struct device *dev);
+static int plip_config(struct device *dev, struct ifmap *map);
+static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
enum plip_connection_state {
- PLIP_CN_NONE=0,
- PLIP_CN_RECEIVE,
- PLIP_CN_SEND,
- PLIP_CN_CLOSING,
- PLIP_CN_ERROR
+ PLIP_CN_NONE=0,
+ PLIP_CN_RECEIVE,
+ PLIP_CN_SEND,
+ PLIP_CN_CLOSING,
+ PLIP_CN_ERROR
};
enum plip_packet_state {
- PLIP_PK_DONE=0,
- PLIP_PK_TRIGGER,
- PLIP_PK_LENGTH_LSB,
- PLIP_PK_LENGTH_MSB,
- PLIP_PK_DATA,
- PLIP_PK_CHECKSUM
+ PLIP_PK_DONE=0,
+ PLIP_PK_TRIGGER,
+ PLIP_PK_LENGTH_LSB,
+ PLIP_PK_LENGTH_MSB,
+ PLIP_PK_DATA,
+ PLIP_PK_CHECKSUM
};
enum plip_nibble_state {
- PLIP_NB_BEGIN,
- PLIP_NB_1,
- PLIP_NB_2,
+ PLIP_NB_BEGIN,
+ PLIP_NB_1,
+ PLIP_NB_2,
};
-#define PLIP_STATE_STRING(x) \
- (((x) == PLIP_PK_DONE)?"0":\
- ((x) == PLIP_PK_TRIGGER)?"t":\
- ((x) == PLIP_PK_LENGTH_LSB)?"l":\
- ((x) == PLIP_PK_LENGTH_MSB)?"m":\
- ((x) == PLIP_PK_DATA)?"d":\
- ((x) == PLIP_PK_CHECKSUM)?"s":"B")
-
struct plip_local {
- enum plip_packet_state state;
- enum plip_nibble_state nibble;
- unsigned short length;
- unsigned short byte;
- unsigned char checksum;
- unsigned char data;
- struct sk_buff *skb;
+ enum plip_packet_state state;
+ enum plip_nibble_state nibble;
+ union {
+ struct {
+#if defined(LITTLE_ENDIAN)
+ unsigned char lsb;
+ unsigned char msb;
+#elif defined(BIG_ENDIAN)
+ unsigned char msb;
+ unsigned char lsb;
+#else
+#error "Please fix the endianness defines in <asm/byteorder.h>"
+#endif
+ } b;
+ unsigned short h;
+ } length;
+ unsigned short byte;
+ unsigned char checksum;
+ unsigned char data;
+ struct sk_buff *skb;
};
struct net_local {
- struct enet_statistics e;
- struct tq_struct immediate;
- struct tq_struct deferred;
- struct plip_local snd_data;
- struct plip_local rcv_data;
- unsigned long trigger_us;
- unsigned long nibble_us;
- unsigned long unit_us;
- enum plip_connection_state connection;
- unsigned short timeout_count;
+ struct enet_statistics enet_stats;
+ struct tq_struct immediate;
+ struct tq_struct deferred;
+ struct plip_local snd_data;
+ struct plip_local rcv_data;
+ unsigned long trigger;
+ unsigned long nibble;
+ enum plip_connection_state connection;
+ unsigned short timeout_count;
+ char is_deferred;
+ int (*orig_rebuild_header)(void *eth, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
};
-
-/* Routines used internally. */
-static void plip_device_clear(struct device *dev);
-static void plip_interrupt(int reg_ptr);
-
-static int plip_error(struct device *dev);
-static int plip_receive_packet(struct device *dev);
-static int plip_send_packet(struct device *dev);
-static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-static int plip_config(struct device *dev, struct ifmap *map);
+/* Entry point of PLIP driver.
+ Probe the hardware, and register/initialize the driver. */
int
plip_init(struct device *dev)
{
- struct net_local *nl;
+ struct net_local *nl;
- /* Check that there is something at base_addr. */
- outb(LP_PINITP, PAR_CONTROL(dev));
- outb(0x00, PAR_DATA(dev));
- if (inb(PAR_DATA(dev)) != 0x00)
- return -ENODEV;
+ /* Check region before the probe */
+ if (check_region(PAR_DATA(dev), 3) < 0)
+ return -ENODEV;
- /* Alpha testers must have the version number to report bugs. */
- if (net_debug)
- printk(version);
+ /* Check that there is something at base_addr. */
+ outb(0, PAR_DATA(dev));
+ udelay(1000);
+ if (inb(PAR_DATA(dev)) != 0)
+ return -ENODEV;
- if (dev->irq) {
- printk("%s: configured for parallel port at %#3x, IRQ %d.\n",
- dev->name, dev->base_addr, dev->irq);
- } else {
- printk("%s: configured for parallel port at %#3x",
- dev->name, dev->base_addr);
+ printk(version);
+ printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
+ if (dev->irq) {
+ printk("using assigned IRQ %d.\n", dev->irq);
+ } else {
+ int irq = 0;
#ifdef MODULE
- /* autoirq doesn't work :(, but we can set it by ifconfig */
+ /* dev->irq==0 means autoprobe, but we don't try to do so
+ with module. We can change it by ifconfig */
#else
- autoirq_setup(0);
- outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
- outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
- outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
- dev->irq = autoirq_report(1);
+ unsigned int irqs = probe_irq_on();
+
+ outb(0x00, PAR_CONTROL(dev));
+ udelay(1000);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ udelay(1000);
+ irq = probe_irq_off(irqs);
#endif
- if (dev->irq)
- printk(", probed IRQ %d.\n", dev->irq);
- else {
- printk(", failed to detect IRQ line.\n");
- return -ENODEV;
+ if (irq > 0) {
+ dev->irq = irq;
+ printk("using probed IRQ %d.\n", dev->irq);
+ } else
+ printk("failed to detect IRQ(%d) --"
+ " Please set IRQ by ifconfig.\n", irq);
}
- }
-
- /* Fill in the generic fields of the device structure. */
- ether_setup(dev);
-
- /* And, override parts of it */
- dev->rebuild_header = plip_rebuild_header;
- 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->flags = IFF_POINTOPOINT;
-
- /* Set private structure */
- dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
- memset(dev->priv, 0, sizeof(struct net_local));
- nl = (struct net_local *) dev->priv;
-
- /* initialize constants */
- nl->trigger_us = PLIP_TRIGGER_WAIT;
- nl->nibble_us = PLIP_NIBBLE_WAIT;
- nl->unit_us = PLIP_DELAY_UNIT;
-
- /* initialize task queue structures */
- nl->immediate.next = &tq_last;
- nl->immediate.sync = 0;
- nl->immediate.routine = (void *)(void *)plip_bh;
- nl->immediate.data = dev;
-
- nl->deferred.next = &tq_last;
- nl->deferred.sync = 0;
- nl->deferred.routine = (void *)(void *)plip_kick_bh;
- nl->deferred.data = dev;
-
- return 0;
+
+ request_region(PAR_DATA(dev), 3, dev->name);
+
+ /* 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->flags = IFF_POINTOPOINT|IFF_NOARP;
+
+ /* Set the private structure */
+ dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return EAGAIN;
+ 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;
+
+ /* Initialize constants */
+ nl->trigger = PLIP_TRIGGER_WAIT;
+ nl->nibble = PLIP_NIBBLE_WAIT;
+
+ /* Initialize task queue structures */
+ nl->immediate.next = &tq_last;
+ nl->immediate.sync = 0;
+ nl->immediate.routine = (void *)(void *)plip_bh;
+ nl->immediate.data = dev;
+
+ nl->deferred.next = &tq_last;
+ nl->deferred.sync = 0;
+ nl->deferred.routine = (void *)(void *)plip_kick_bh;
+ nl->deferred.data = dev;
+
+ return 0;
}
+/* Bottom half handler for the delayed request.
+ This routine is kicked by do_timer().
+ Request `plip_bh' to be invoked. */
static void
plip_kick_bh(struct device *dev)
{
- struct net_local *nl = (struct net_local *)dev->priv;
+ struct net_local *nl = (struct net_local *)dev->priv;
- if (nl->connection == PLIP_CN_NONE)
- return;
- queue_task(&nl->immediate, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
- return;
+ if (nl->is_deferred) {
+ queue_task(&nl->immediate, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
}
+/* Forward declarations of internal routines */
+static int plip_none(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_receive_packet(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_send_packet(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_connection_close(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_error(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+ struct plip_local *snd,
+ struct plip_local *rcv,
+ int error);
+
+#define OK 0
+#define TIMEOUT 1
+#define ERROR 2
+
+typedef int (*plip_func)(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv);
+
+static plip_func connection_state_table[] =
+{
+ plip_none,
+ plip_receive_packet,
+ plip_send_packet,
+ plip_connection_close,
+ plip_error
+};
+
+/* Bottom half handler of PLIP. */
static void
plip_bh(struct device *dev)
{
- struct net_local *nl = (struct net_local *)dev->priv;
- struct enet_statistics *stats = (struct enet_statistics *) dev->priv;
- struct plip_local *rcv = &nl->rcv_data;
- struct plip_local *snd = &nl->snd_data;
- int result, timeout=0;
- unsigned char *s;
- unsigned char c0;
- struct sk_buff *skb;
-
- while (!timeout) {
- cli();
- switch (nl->connection) {
- case PLIP_CN_NONE:
- sti();
- return;
-
- case PLIP_CN_RECEIVE:
- sti();
- disable_irq(dev->irq);
- dev->interrupt = 0;
- result = plip_receive_packet(dev);
- if (result == 0) { /* success */
- outb (0x00, PAR_DATA(dev));
- skb = rcv->skb;
- rcv->skb = NULL;
- stats->rx_packets++;
- netif_rx(skb);
- if (snd->state != PLIP_PK_DONE) {
- nl->connection = PLIP_CN_SEND;
- enable_irq(dev->irq);
- } else {
- nl->connection = PLIP_CN_NONE;
- enable_irq(dev->irq);
- return;
- }
- } else if (result == -1) { /* failure */
- outb(0x00, PAR_DATA(dev));
- if (rcv->skb)
- dev_kfree_skb(rcv->skb, FREE_WRITE);
- rcv->state = PLIP_PK_DONE;
- rcv->skb = NULL;
- if (snd->skb)
- dev_kfree_skb(snd->skb, FREE_WRITE);
- snd->state = PLIP_PK_DONE;
- snd->skb = NULL;
- dev->tbusy = 1;
- nl->connection = PLIP_CN_ERROR;
- } else
- timeout = 1;
- break;
-
- case PLIP_CN_SEND:
- sti();
- result = plip_send_packet(dev);
- if (result == -1) /* interrupted */
- break;
- if (result == 0) { /* success */
- outb (0x00, PAR_DATA(dev));
- snd->state = PLIP_PK_DONE;
- snd->skb = NULL;
- nl->connection = PLIP_CN_CLOSING;
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *snd = &nl->snd_data;
+ struct plip_local *rcv = &nl->rcv_data;
+ plip_func f;
+ int r;
+
+ nl->is_deferred = 0;
+ f = connection_state_table[nl->connection];
+ if ((r = (*f)(dev, nl, snd, rcv)) != OK
+ && (r = plip_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
+ nl->is_deferred = 1;
queue_task(&nl->deferred, &tq_timer);
- enable_irq(dev->irq);
- return;
- } else
- timeout = 1;
- break;
-
- case PLIP_CN_CLOSING:
- sti();
- nl->connection = PLIP_CN_NONE;
- mark_bh(NET_BH);
- dev->tbusy = 0;
- return;
-
- case PLIP_CN_ERROR:
- sti();
- result = plip_error(dev);
- if (result == 0) {
- nl->connection = PLIP_CN_NONE;
- dev->tbusy = 0;
- enable_irq(dev->irq);
- return;
- } else {
- queue_task(&nl->deferred, &tq_timer);
- return;
- }
- break;
}
- }
+}
- /* timeout */
- if (++nl->timeout_count > 3) { /* cable problem? */
- c0 = inb(PAR_STATUS(dev));
+static int
+plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv,
+ int error)
+{
+ unsigned char c0;
+ cli();
if (nl->connection == PLIP_CN_SEND) {
- stats->tx_errors++;
- stats->tx_aborted_errors++;
- s = PLIP_STATE_STRING(snd->state);
- if (net_debug > 1)
- printk("%s: transmit timeout(%s,%02x)... reset interface.\n",
- dev->name, s, (unsigned int)c0);
- if (snd->skb)
- dev_kfree_skb(snd->skb, FREE_WRITE);
+
+ if (error != ERROR) { /* Timeout */
+ nl->timeout_count++;
+ if ((snd->state == PLIP_PK_TRIGGER
+ && nl->timeout_count <= 10)
+ || nl->timeout_count <= 3) {
+ sti();
+ /* Try again later */
+ return TIMEOUT;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ printk("%s: transmit timeout(%d,%02x)\n",
+ dev->name, snd->state, c0);
+ }
+ nl->enet_stats.tx_errors++;
+ nl->enet_stats.tx_aborted_errors++;
} else if (nl->connection == PLIP_CN_RECEIVE) {
- stats->rx_dropped++;
- s = PLIP_STATE_STRING(rcv->state);
- if (net_debug > 1)
- printk("%s: receive timeout(%s,%02x)... reset interface.\n",
- dev->name, s, (unsigned int)c0);
- if (rcv->skb)
- dev_kfree_skb(rcv->skb, FREE_WRITE);
+ if (rcv->state == PLIP_PK_TRIGGER) {
+ /* Transmission was interrupted. */
+ sti();
+ return OK;
+ }
+ if (error != ERROR) { /* Timeout */
+ if (++nl->timeout_count <= 3) {
+ sti();
+ /* Try again later */
+ return TIMEOUT;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ printk("%s: receive timeout(%d,%02x)\n",
+ dev->name, rcv->state, c0);
+ }
+ nl->enet_stats.rx_dropped++;
+ }
+ rcv->state = PLIP_PK_DONE;
+ if (rcv->skb) {
+ rcv->skb->free = 1;
+ kfree_skb(rcv->skb, FREE_READ);
+ rcv->skb = NULL;
+ }
+ snd->state = PLIP_PK_DONE;
+ if (snd->skb) {
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ snd->skb = NULL;
}
disable_irq(dev->irq);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
dev->tbusy = 1;
nl->connection = PLIP_CN_ERROR;
outb(0x00, PAR_DATA(dev));
- }
+ sti();
- queue_task(&nl->deferred, &tq_timer);
- return;
+ return TIMEOUT;
}
static int
-plip_tx_packet(struct sk_buff *skb, struct device *dev)
+plip_none(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- struct net_local *nl = (struct net_local *)dev->priv;
- struct plip_local *snd = &nl->snd_data;
-
- if (dev->tbusy)
- return 1;
-
- /* If some higher layer thinks we've missed an tx-done interrupt
- we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- itself. */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
+ return OK;
+}
- if (set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- return 1;
- }
+/* 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,
+ enum plip_nibble_state *ns_p, unsigned char *data_p)
+{
+ unsigned char c0, c1;
+ unsigned int cx;
+
+ switch (*ns_p) {
+ case PLIP_NB_BEGIN:
+ cx = nibble_timeout;
+ while (1) {
+ c0 = inb(status_addr);
+ udelay(PLIP_DELAY_UNIT);
+ if ((c0 & 0x80) == 0) {
+ c1 = inb(status_addr);
+ if (c0 == c1)
+ break;
+ }
+ if (--cx == 0)
+ return TIMEOUT;
+ }
+ *data_p = (c0 >> 3) & 0x0f;
+ outb(0x10, --status_addr); /* send ACK */
+ status_addr++;
+ *ns_p = PLIP_NB_1;
+
+ case PLIP_NB_1:
+ cx = nibble_timeout;
+ while (1) {
+ c0 = inb(status_addr);
+ udelay(PLIP_DELAY_UNIT);
+ if (c0 & 0x80) {
+ c1 = inb(status_addr);
+ if (c0 == c1)
+ break;
+ }
+ if (--cx == 0)
+ return TIMEOUT;
+ }
+ *data_p |= (c0 << 1) & 0xf0;
+ outb(0x00, --status_addr); /* send ACK */
+ status_addr++;
+ *ns_p = PLIP_NB_BEGIN;
+ return OK;
- if (skb->len > dev->mtu) {
- printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
- dev->tbusy = 0;
- return 0;
- }
+ case PLIP_NB_2:
+ }
+}
- snd->state = PLIP_PK_TRIGGER;
- dev->trans_start = jiffies;
+/* PLIP_RECEIVE_PACKET --- receive a packet */
+static int
+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;
- snd->skb = skb;
- snd->length = skb->len;
+ switch (rcv->state) {
+ case PLIP_PK_TRIGGER:
+ disable_irq(dev->irq);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ dev->interrupt = 0;
+ outb(0x01, PAR_DATA(dev)); /* send ACK */
+ if (net_debug > 2)
+ printk("%s: receive start\n", dev->name);
+ rcv->state = PLIP_PK_LENGTH_LSB;
+ rcv->nibble = PLIP_NB_BEGIN;
+
+ case PLIP_PK_LENGTH_LSB:
+ if (snd->state != PLIP_PK_DONE) {
+ if (plip_receive(nl->trigger, status_addr,
+ &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_irq(dev->irq);
+ return OK;
+ }
+ } else {
+ if (plip_receive(nibble_timeout, status_addr,
+ &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,
+ &rcv->nibble, &rcv->length.b.msb))
+ return TIMEOUT;
+ if (rcv->length.h > dev->mtu || rcv->length.h < 8) {
+ printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
+ return ERROR;
+ }
+ /* Malloc up new buffer. */
+ rcv->skb = alloc_skb(rcv->length.h, GFP_ATOMIC);
+ if (rcv->skb == NULL) {
+ printk("%s: Memory squeeze.\n", dev->name);
+ return ERROR;
+ }
+ rcv->skb->len = rcv->length.h;
+ rcv->skb->dev = dev;
+ rcv->state = PLIP_PK_DATA;
+ rcv->byte = 0;
+ rcv->checksum = 0;
+
+ case PLIP_PK_DATA:
+ lbuf = rcv->skb->data;
+ do
+ if (plip_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &lbuf[rcv->byte]))
+ return TIMEOUT;
+ while (++rcv->byte < rcv->length.h);
+ do
+ rcv->checksum += lbuf[--rcv->byte];
+ while (rcv->byte);
+ rcv->state = PLIP_PK_CHECKSUM;
+
+ case PLIP_PK_CHECKSUM:
+ if (plip_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &rcv->data))
+ return TIMEOUT;
+ if (rcv->data != rcv->checksum) {
+ nl->enet_stats.rx_crc_errors++;
+ if (net_debug)
+ printk("%s: checksum error\n", dev->name);
+ return ERROR;
+ }
+ rcv->state = PLIP_PK_DONE;
- cli();
- if (nl->connection == PLIP_CN_NONE) {
- nl->connection = PLIP_CN_SEND;
- nl->timeout_count = 0;
- }
- sti();
- queue_task(&nl->immediate, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
+ case PLIP_PK_DONE:
+ /* Inform the upper layer for the arrival of a packet. */
+ rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
+ netif_rx(rcv->skb);
+ nl->enet_stats.rx_packets++;
+ rcv->skb = NULL;
+ if (net_debug > 2)
+ printk("%s: receive end\n", dev->name);
- return 0;
+ /* Close the connection. */
+ outb (0x00, PAR_DATA(dev));
+ cli();
+ if (snd->state != PLIP_PK_DONE) {
+ nl->connection = PLIP_CN_SEND;
+ sti();
+ queue_task(&nl->immediate, &tq_immediate);
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ } else {
+ nl->connection = PLIP_CN_NONE;
+ sti();
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+ }
+ return OK;
}
-/* Open/initialize the board. This is called (in the current kernel)
- sometime after booting when the 'ifconfig' program is run.
-
- This routine gets exclusive access to the parallel port by allocating
- its IRQ line.
- */
-static int
-plip_open(struct device *dev)
+/* 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,
+ enum plip_nibble_state *ns_p, unsigned char data)
{
- int i;
-
- cli();
- if (request_irq(dev->irq , plip_interrupt, 0, "plip") != 0) {
- sti();
- printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
- return -EAGAIN;
- }
- irq2dev_map[dev->irq] = dev;
- sti();
- /* enable rx interrupt. */
- outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
- plip_device_clear(dev);
-
- /* Fill in the MAC-level header. */
- for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
- dev->dev_addr[i] = 0xfc;
- memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(unsigned long));
-
- dev->start = 1;
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
- return 0;
+ unsigned char c0;
+ unsigned int cx;
+
+ switch (*ns_p) {
+ case PLIP_NB_BEGIN:
+ outb((data & 0x0f), data_addr);
+ *ns_p = PLIP_NB_1;
+
+ case PLIP_NB_1:
+ outb(0x10 | (data & 0x0f), data_addr);
+ cx = nibble_timeout;
+ data_addr++;
+ while (1) {
+ c0 = inb(data_addr);
+ if ((c0 & 0x80) == 0)
+ break;
+ if (--cx == 0)
+ return TIMEOUT;
+ udelay(PLIP_DELAY_UNIT);
+ }
+ outb(0x10 | (data >> 4), --data_addr);
+ *ns_p = PLIP_NB_2;
+
+ case PLIP_NB_2:
+ outb((data >> 4), data_addr);
+ data_addr++;
+ cx = nibble_timeout;
+ while (1) {
+ c0 = inb(data_addr);
+ if (c0 & 0x80)
+ break;
+ if (--cx == 0)
+ return TIMEOUT;
+ udelay(PLIP_DELAY_UNIT);
+ }
+ data_addr--;
+ *ns_p = PLIP_NB_BEGIN;
+ return OK;
+ }
}
-/* The inverse routine to plip_open (). */
+/* PLIP_SEND_PACKET --- send a packet */
static int
-plip_close(struct device *dev)
+plip_send_packet(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- dev->tbusy = 1;
- dev->start = 0;
- cli();
- free_irq(dev->irq);
- irq2dev_map[dev->irq] = NULL;
- sti();
- outb(0x00, PAR_DATA(dev));
- /* release the interrupt. */
- outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif
- return 0;
+ unsigned short data_addr = PAR_DATA(dev);
+ unsigned short nibble_timeout = nl->nibble;
+ unsigned char *lbuf;
+ unsigned char c0;
+ unsigned int cx;
+
+ if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
+ printk("%s: send skb lost\n", dev->name);
+ snd->state = PLIP_PK_DONE;
+ snd->skb = NULL;
+ return ERROR;
+ }
+
+ switch (snd->state) {
+ case PLIP_PK_TRIGGER:
+ /* Trigger remote rx interrupt. */
+ outb(0x08, data_addr);
+ cx = nl->trigger;
+ while (1) {
+ udelay(PLIP_DELAY_UNIT);
+ cli();
+ if (nl->connection == PLIP_CN_RECEIVE) {
+ sti();
+ /* interrupted */
+ nl->enet_stats.collisions++;
+ if (net_debug > 1)
+ printk("%s: collision.\n", dev->name);
+ return OK;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ if (c0 & 0x08) {
+ disable_irq(dev->irq);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ if (net_debug > 2)
+ printk("%s: send start\n", dev->name);
+ snd->state = PLIP_PK_LENGTH_LSB;
+ snd->nibble = PLIP_NB_BEGIN;
+ nl->timeout_count = 0;
+ sti();
+ break;
+ }
+ sti();
+ if (--cx == 0) {
+ outb(0x00, data_addr);
+ return TIMEOUT;
+ }
+ }
+
+ case PLIP_PK_LENGTH_LSB:
+ if (plip_send(nibble_timeout, data_addr,
+ &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,
+ &snd->nibble, snd->length.b.msb))
+ return TIMEOUT;
+ snd->state = PLIP_PK_DATA;
+ snd->byte = 0;
+ snd->checksum = 0;
+
+ case PLIP_PK_DATA:
+ do
+ if (plip_send(nibble_timeout, data_addr,
+ &snd->nibble, lbuf[snd->byte]))
+ return TIMEOUT;
+ while (++snd->byte < snd->length.h);
+ do
+ snd->checksum += lbuf[--snd->byte];
+ while (snd->byte);
+ snd->state = PLIP_PK_CHECKSUM;
+
+ case PLIP_PK_CHECKSUM:
+ if (plip_send(nibble_timeout, data_addr,
+ &snd->nibble, snd->checksum))
+ return TIMEOUT;
+
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ nl->enet_stats.tx_packets++;
+ snd->state = PLIP_PK_DONE;
+
+ case PLIP_PK_DONE:
+ /* Close the connection */
+ outb (0x00, data_addr);
+ snd->skb = NULL;
+ if (net_debug > 2)
+ printk("%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_irq(dev->irq);
+ return OK;
+ }
+ return OK;
}
-static struct enet_statistics *
-plip_get_stats(struct device *dev)
+static int
+plip_connection_close(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- struct enet_statistics *localstats = (struct enet_statistics*)dev->priv;
- return localstats;
+ cli();
+ if (nl->connection == PLIP_CN_CLOSING) {
+ nl->connection = PLIP_CN_NONE;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ sti();
+ return OK;
}
-/* We don't need to send arp, for plip is point-to-point. */
+/* PLIP_ERROR --- wait till other end settled */
static int
-plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
- struct sk_buff *skb)
+plip_error(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- struct ethhdr *eth = (struct ethhdr *)buff;
- int i;
+ unsigned char status;
- if (eth->h_proto != htons(ETH_P_IP)) {
- printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- return 0;
- }
+ status = inb(PAR_STATUS(dev));
+ if ((status & 0xf8) == 0x80) {
+ if (net_debug > 2)
+ printk("%s: reset interface.\n", dev->name);
+ nl->connection = PLIP_CN_NONE;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ mark_bh(NET_BH);
+ } else {
+ nl->is_deferred = 1;
+ queue_task(&nl->deferred, &tq_timer);
+ }
- for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
- eth->h_dest[i] = 0xfc;
- memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
- return 0;
+ return OK;
}
+/* Handle the parallel port interrupts. */
static void
-plip_device_clear(struct device *dev)
+plip_interrupt(int irq, struct pt_regs * regs)
{
- struct net_local *nl = (struct net_local *)dev->priv;
-
- outb (0x00, PAR_DATA(dev));
- nl->rcv_data.state = PLIP_PK_DONE;
- nl->snd_data.state = PLIP_PK_DONE;
- nl->rcv_data.skb = NULL;
- nl->snd_data.skb = NULL;
- nl->connection = PLIP_CN_NONE;
- cli();
- dev->tbusy = 0;
- sti();
- enable_irq(dev->irq);
-}
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *rcv = &nl->rcv_data;
+ unsigned char c0;
-/* PLIP_ERROR --- wait til other end settled */
-static int
-plip_error(struct device *dev)
-{
- unsigned char status;
+ if (dev == NULL) {
+ printk ("plip_interrupt: irq %d for unknown device.\n", irq);
+ return;
+ }
- status = inb(PAR_STATUS(dev));
- if ((status & 0xf8) == 0x80)
- return 0;
- return 1;
+ if (dev->interrupt)
+ return;
+
+ c0 = inb(PAR_STATUS(dev));
+ if ((c0 & 0xf8) != 0xc0) {
+ if (net_debug > 1)
+ printk("%s: spurious interrupt\n", dev->name);
+ return;
+ }
+ dev->interrupt = 1;
+ if (net_debug > 3)
+ printk("%s: interrupt.\n", dev->name);
+
+ cli();
+ switch (nl->connection) {
+ case PLIP_CN_CLOSING:
+ dev->tbusy = 0;
+ case PLIP_CN_NONE:
+ case PLIP_CN_SEND:
+ dev->last_rx = jiffies;
+ rcv->state = PLIP_PK_TRIGGER;
+ nl->connection = PLIP_CN_RECEIVE;
+ nl->timeout_count = 0;
+ queue_task(&nl->immediate, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ sti();
+ break;
+
+ case PLIP_CN_RECEIVE:
+ sti();
+ printk("%s: receive interrupt when receiving packet\n", dev->name);
+ break;
+
+ case PLIP_CN_ERROR:
+ sti();
+ printk("%s: receive interrupt in error state\n", dev->name);
+ break;
+ }
}
-/* PLIP_RECEIVE --- receive a byte(two nibbles)
- Returns 0 on success, 1 on failure */
-inline static int
-plip_receive(unsigned short nibble_timeout, unsigned short unit_us,
- unsigned short status_addr, unsigned short data_addr,
- enum plip_nibble_state *ns_p, unsigned char *data_p)
+/* We don't need to send arp, for plip is point-to-point. */
+static int
+plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+ struct sk_buff *skb)
{
- unsigned char c0, c1;
- unsigned int cx;
-
- switch (*ns_p) {
- case PLIP_NB_BEGIN:
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- udelay(unit_us);
- if ((c0 & 0x80) == 0) {
- c1 = inb(status_addr);
- if (c0 == c1)
- break;
- }
- if (--cx == 0)
- return 1;
- }
- *data_p = (c0 >> 3) & 0x0f;
- outb(0x10, data_addr); /* send ACK */
- *ns_p = PLIP_NB_1;
-
- case PLIP_NB_1:
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- udelay(unit_us);
- if (c0 & 0x80) {
- c1 = inb(status_addr);
- if (c0 == c1)
- break;
- }
- if (--cx == 0)
- return 1;
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct ethhdr *eth = (struct ethhdr *)buff;
+ int i;
+
+ if ((dev->flags & IFF_NOARP)==0)
+ return nl->orig_rebuild_header(buff, dev, dst, skb);
+
+ if (eth->h_proto != htons(ETH_P_IP)) {
+ printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return 0;
}
- *data_p |= (c0 << 1) & 0xf0;
- outb(0x00, data_addr); /* send ACK */
- *ns_p = PLIP_NB_BEGIN;
- return 0;
- case PLIP_NB_2:
- }
+ for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+ eth->h_dest[i] = 0xfc;
+ memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
+ return 0;
}
-/* PLIP_RECEIVE_PACKET --- receive a packet
- Returns 0 on success, 1 when timeout, -1 on failure */
static int
-plip_receive_packet(struct device *dev)
+plip_tx_packet(struct sk_buff *skb, struct device *dev)
{
- unsigned short data_addr = PAR_DATA(dev), status_addr = PAR_STATUS(dev);
- struct net_local *nl = (struct net_local *)dev->priv;
- unsigned short nibble_timeout = nl->nibble_us, unit_us = nl->unit_us;
- struct plip_local *rcv = &nl->rcv_data;
- unsigned char *lbuf;
- struct enet_statistics *stats = (struct enet_statistics *) dev->priv;
-
- switch (rcv->state) {
- case PLIP_PK_TRIGGER:
- rcv->state = PLIP_PK_LENGTH_LSB;
- rcv->nibble = PLIP_NB_BEGIN;
-
- case PLIP_PK_LENGTH_LSB:
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, (unsigned char *)&rcv->length))
- return 1;
- rcv->state = PLIP_PK_LENGTH_MSB;
-
- case PLIP_PK_LENGTH_MSB:
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, (unsigned char *)&rcv->length+1))
- return 1;
- if (rcv->length > dev->mtu || rcv->length < 8) {
- printk("%s: bogus packet size %d.\n", dev->name, rcv->length);
- return -1;
- }
- /* Malloc up new buffer. */
- rcv->skb = alloc_skb(rcv->length, GFP_ATOMIC);
- if (rcv->skb == NULL) {
- printk("%s: Memory squeeze.\n", dev->name);
- return -1;
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *snd = &nl->snd_data;
+
+ if (dev->tbusy)
+ return 1;
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
}
- rcv->skb->len = rcv->length;
- rcv->skb->dev = dev;
- rcv->state = PLIP_PK_DATA;
- rcv->byte = 0;
- rcv->checksum = 0;
-
- case PLIP_PK_DATA:
- lbuf = rcv->skb->data;
- do {
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, &lbuf[rcv->byte]))
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
return 1;
- rcv->checksum += lbuf[rcv->byte];
- } while (++rcv->byte < rcv->length);
- rcv->state = PLIP_PK_CHECKSUM;
-
- case PLIP_PK_CHECKSUM:
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, &rcv->data))
- return 1;
- if (rcv->data != rcv->checksum) {
- stats->rx_crc_errors++;
- if (net_debug)
- printk("%s: checksum error\n", dev->name);
- return -1;
}
- rcv->state = PLIP_PK_DONE;
- case PLIP_PK_DONE:
- }
- return 0;
-}
+ if (skb->len > dev->mtu) {
+ printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
+ dev->tbusy = 0;
+ return 0;
+ }
-/* Handle the parallel port interrupts. */
-static void
-plip_interrupt(int reg_ptr)
-{
- int irq = pt_regs2irq(reg_ptr);
- struct device *dev = (struct device *) irq2dev_map[irq];
- struct net_local *nl = (struct net_local *)dev->priv;
- struct plip_local *rcv = &nl->rcv_data;
- unsigned char c0;
-
- if (dev == NULL) {
- if (net_debug)
- printk ("plip_interrupt: irq %d for unknown device.\n", irq);
- return;
- }
-
- if (dev->interrupt)
- return;
-
- c0 = inb(PAR_STATUS(dev));
- if ((c0 & 0xf8) != 0xc0) {
- if (net_debug > 3)
- printk("plip: spurious interrupt\n");
- return;
- }
- outb(0x01, PAR_DATA(dev)); /* send ACK */
- dev->interrupt = 1;
- if (net_debug > 3)
- printk("%s: interrupt.\n", dev->name);
-
- cli();
- switch (nl->connection) {
- case PLIP_CN_CLOSING:
- dev->tbusy = 0;
- case PLIP_CN_NONE:
- case PLIP_CN_SEND:
- sti();
- dev->last_rx = jiffies;
- rcv->state = PLIP_PK_TRIGGER;
- nl->connection = PLIP_CN_RECEIVE;
- nl->timeout_count = 0;
+ if (net_debug > 2)
+ printk("%s: send request\n", dev->name);
+
+ cli();
+ dev->trans_start = jiffies;
+ snd->skb = skb;
+ snd->length.h = skb->len;
+ snd->state = PLIP_PK_TRIGGER;
+ if (nl->connection == PLIP_CN_NONE) {
+ nl->connection = PLIP_CN_SEND;
+ nl->timeout_count = 0;
+ }
queue_task(&nl->immediate, &tq_immediate);
mark_bh(IMMEDIATE_BH);
- break;
-
- case PLIP_CN_RECEIVE:
sti();
- printk("%s: receive interrupt when receiving packet\n", dev->name);
- break;
- case PLIP_CN_ERROR:
- sti();
- printk("%s: receive interrupt in error state\n", dev->name);
- break;
- }
+ return 0;
}
-
-/* PLIP_SEND --- send a byte (two nibbles)
- Returns 0 on success, 1 on failure */
-inline static int
-plip_send(unsigned short nibble_timeout, unsigned short unit_us,
- unsigned short status_addr, unsigned short data_addr,
- enum plip_nibble_state *ns_p, unsigned char data)
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine gets exclusive access to the parallel port by allocating
+ its IRQ line.
+ */
+static int
+plip_open(struct device *dev)
{
- unsigned char c0;
- unsigned int cx;
-
- switch (*ns_p) {
- case PLIP_NB_BEGIN:
- outb((data & 0x0f), data_addr);
- *ns_p = PLIP_NB_1;
-
- case PLIP_NB_1:
- outb(0x10 | (data & 0x0f), data_addr);
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- if ((c0 & 0x80) == 0)
- break;
- if (--cx == 0) /* time out */
- return 1;
- udelay(unit_us);
+ struct net_local *nl = (struct net_local *)dev->priv;
+ int i;
+
+ if (dev->irq == 0) {
+ printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name);
+ return -EAGAIN;
}
- outb(0x10 | (data >> 4), data_addr);
- *ns_p = PLIP_NB_2;
-
- case PLIP_NB_2:
- outb((data >> 4), data_addr);
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- if (c0 & 0x80)
- break;
- if (--cx == 0) /* time out */
- return 1;
- udelay(unit_us);
+ cli();
+ if (request_irq(dev->irq , plip_interrupt, 0, dev->name) != 0) {
+ sti();
+ printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
}
- *ns_p = PLIP_NB_BEGIN;
+ irq2dev_map[dev->irq] = dev;
+ sti();
+
+ /* Clear the data port. */
+ outb (0x00, PAR_DATA(dev));
+
+ /* Enable rx interrupt. */
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+
+ /* Initialize the state machine. */
+ nl->rcv_data.state = nl->snd_data.state = PLIP_PK_DONE;
+ nl->rcv_data.skb = nl->snd_data.skb = NULL;
+ nl->connection = PLIP_CN_NONE;
+ nl->is_deferred = 0;
+
+ /* Fill in the MAC-level header. */
+ for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+ dev->dev_addr[i] = 0xfc;
+ memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(unsigned long));
+
+ dev->interrupt = 0;
+ dev->start = 1;
+ dev->tbusy = 0;
+ MOD_INC_USE_COUNT;
return 0;
- }
}
-/* PLIP_SEND_PACKET --- send a packet
- Returns 0 on success, 1 when timeout, -1 when interrupted */
+/* The inverse routine to plip_open (). */
static int
-plip_send_packet(struct device *dev)
+plip_close(struct device *dev)
{
- unsigned short data_addr = PAR_DATA(dev), status_addr = PAR_STATUS(dev);
- struct net_local *nl = (struct net_local *)dev->priv;
- unsigned short nibble_timeout = nl->nibble_us, unit_us = nl->unit_us;
- struct plip_local *snd = &nl->snd_data;
- unsigned char *lbuf = snd->skb->data;
- unsigned char c0;
- unsigned int cx;
- struct enet_statistics *stats = (struct enet_statistics *) dev->priv;
-
- switch (snd->state) {
- case PLIP_PK_TRIGGER:
- /* Trigger remote rx interrupt. */
- outb(0x08, PAR_DATA(dev));
- cx = nl->trigger_us;
- while (1) {
- if (nl->connection == PLIP_CN_RECEIVE) { /* interrupted */
- stats->collisions++;
- if (net_debug > 3)
- printk("%s: collision.\n", dev->name);
- return -1;
- }
- cli();
- c0 = inb(PAR_STATUS(dev));
- if (c0 & 0x08) {
- disable_irq(dev->irq);
- if (net_debug > 3)
- printk("+");
- /* OK, connection established! */
- snd->state = PLIP_PK_LENGTH_LSB;
- snd->nibble = PLIP_NB_BEGIN;
- nl->timeout_count = 0;
- sti();
- break;
- }
- sti();
- udelay(nl->unit_us);
- if (--cx == 0) {
- outb(0x00, PAR_DATA(dev));
- return 1;
- }
- }
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *snd = &nl->snd_data;
+ struct plip_local *rcv = &nl->rcv_data;
- case PLIP_PK_LENGTH_LSB:
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, snd->length & 0xff)) /* timeout */
- return 1;
- snd->state = PLIP_PK_LENGTH_MSB;
-
- case PLIP_PK_LENGTH_MSB:
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, snd->length >> 8)) /* timeout */
- return 1;
- snd->state = PLIP_PK_DATA;
- snd->byte = 0;
- snd->checksum = 0;
-
- case PLIP_PK_DATA:
- do {
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, lbuf[snd->byte])) /* timeout */
- return 1;
- snd->checksum += lbuf[snd->byte];
- } while (++snd->byte < snd->length);
- snd->state = PLIP_PK_CHECKSUM;
+ dev->tbusy = 1;
+ dev->start = 0;
+ cli();
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ nl->is_deferred = 0;
+ nl->connection = PLIP_CN_NONE;
+ sti();
+ outb(0x00, PAR_DATA(dev));
+
+ snd->state = PLIP_PK_DONE;
+ if (snd->skb) {
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ snd->skb = NULL;
+ }
+ rcv->state = PLIP_PK_DONE;
+ if (rcv->skb) {
+ rcv->skb->free = 1;
+ kfree_skb(rcv->skb, FREE_READ);
+ rcv->skb = NULL;
+ }
- case PLIP_PK_CHECKSUM:
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, snd->checksum)) /* timeout */
- return 1;
+ /* Reset. */
+ outb(0x00, PAR_CONTROL(dev));
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
- dev_kfree_skb(snd->skb, FREE_WRITE);
- stats->tx_packets++;
+static struct enet_statistics *
+plip_get_stats(struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct enet_statistics *r = &nl->enet_stats;
- case PLIP_PK_DONE:
- }
- return 0;
+ return r;
}
-
-static int plip_config(struct device *dev, struct ifmap *map)
+
+static int
+plip_config(struct device *dev, struct ifmap *map)
{
- if(dev->flags&IFF_UP)
+ if (dev->flags & IFF_UP)
return -EBUSY;
-/*
- * We could probe this for verification, but since they told us
- * to do it then they can suffer.
- */
- if(map->base_addr!= (unsigned short)-1)
- dev->base_addr=map->base_addr;
- if(map->irq!= (unsigned char)-1)
- dev->irq= map->irq;
+
+ if (map->base_addr != (unsigned long)-1
+ && map->base_addr != dev->base_addr)
+ printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
+
+ if (map->irq != (unsigned char)-1)
+ dev->irq = map->irq;
return 0;
}
-static int plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int
+plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
struct net_local *nl = (struct net_local *) dev->priv;
struct plipconf *pc = (struct plipconf *) &rq->ifr_data;
- switch(pc->pcmd)
- {
- case PLIP_GET_TIMEOUT:
- pc->trigger=nl->trigger_us;
- pc->nibble=nl->nibble_us;
- pc->unit=nl->unit_us;
- break;
- case PLIP_SET_TIMEOUT:
- nl->trigger_us=pc->trigger;
- nl->nibble_us=pc->nibble;
- nl->unit_us=pc->unit;
- break;
- default:
- return -EOPNOTSUPP;
+ switch(pc->pcmd) {
+ case PLIP_GET_TIMEOUT:
+ pc->trigger = nl->trigger;
+ pc->nibble = nl->nibble;
+ break;
+ case PLIP_SET_TIMEOUT:
+ nl->trigger = pc->trigger;
+ nl->nibble = pc->nibble;
+ break;
+ default:
+ return -EOPNOTSUPP;
}
return 0;
}
-
#ifdef MODULE
char kernel_version[] = UTS_RELEASE;
@@ -931,53 +1061,45 @@ static struct device dev_plip2 =
int
init_module(void)
{
- int devices=0;
-
- if (register_netdev(&dev_plip0) != 0)
- devices++;
- if (register_netdev(&dev_plip1) != 0)
- devices++;
- if (register_netdev(&dev_plip2) != 0)
- devices++;
- if (devices == 0)
- return -EIO;
- return 0;
+ int devices=0;
+
+ if (register_netdev(&dev_plip0) != 0)
+ devices++;
+ if (register_netdev(&dev_plip1) != 0)
+ devices++;
+ if (register_netdev(&dev_plip2) != 0)
+ devices++;
+ if (devices == 0)
+ return -EIO;
+ return 0;
}
void
cleanup_module(void)
{
- if (MOD_IN_USE)
- printk("plip: device busy, remove delayed\n");
- else {
if (dev_plip0.priv) {
- unregister_netdev(&dev_plip0);
- kfree_s(dev_plip0.priv, sizeof(struct net_local));
- dev_plip0.priv = NULL;
+ unregister_netdev(&dev_plip0);
+ release_region(PAR_DATA(&dev_plip0), 3);
+ kfree_s(dev_plip0.priv, sizeof(struct net_local));
+ dev_plip0.priv = NULL;
}
if (dev_plip1.priv) {
- unregister_netdev(&dev_plip1);
- kfree_s(dev_plip1.priv, sizeof(struct net_local));
- dev_plip1.priv = NULL;
+ unregister_netdev(&dev_plip1);
+ release_region(PAR_DATA(&dev_plip1), 3);
+ kfree_s(dev_plip1.priv, sizeof(struct net_local));
+ dev_plip1.priv = NULL;
}
if (dev_plip2.priv) {
- unregister_netdev(&dev_plip2);
- kfree_s(dev_plip2.priv, sizeof(struct net_local));
- dev_plip2.priv = NULL;
+ unregister_netdev(&dev_plip2);
+ release_region(PAR_DATA(&dev_plip2), 3);
+ kfree_s(dev_plip2.priv, sizeof(struct net_local));
+ dev_plip2.priv = NULL;
}
- }
}
#endif /* MODULE */
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -m486 -I../../net/inet -c plip.c"
- * c-indent-level: 4
- * c-continued-statement-offset: 4
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * version-control: t
- * kept-new-versions: 10
+ * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c plip.c"
* End:
*/