summaryrefslogtreecommitdiffstats
path: root/drivers/net/plip.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /drivers/net/plip.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'drivers/net/plip.c')
-rw-r--r--drivers/net/plip.c417
1 files changed, 263 insertions, 154 deletions
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index 9fbf8cf49..25c33ab1a 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -1,4 +1,4 @@
-/* $Id: plip.c,v 1.16 1996-04-06 15:36:57+09 gniibe Exp $ */
+/* $Id: plip.c,v 1.3.6.2 1997/04/16 15:07:56 phil Exp $ */
/* PLIP: A parallel port "network" driver for Linux. */
/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
/*
@@ -11,16 +11,9 @@
*
* Modularization and ifreq/ifmap support by Alan Cox.
* Rewritten by Niibe Yutaka.
+ * parport-sharing awareness code by Philip Blundell.
*
* Fixes:
- * 9-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
- * - only claim 3 bytes of I/O space for port at 0x3bc
- * - treat NULL return from register_netdev() as success in
- * init_module()
- * - added message if driver loaded as a module but no
- * interfaces present.
- * - release claimed I/O ports if malloc() fails during init.
- *
* Niibe Yutaka
* - Module initialization. You can specify I/O addr and IRQ:
* # insmod plip.o io=0x3bc irq=7
@@ -44,7 +37,7 @@
* Crynwr packet driver, Peter Bauer changed the protocol again
* back to original protocol.
*
- * This version follows original PLIP protocol.
+ * This version follows original PLIP protocol.
* So, this PLIP can't communicate the PLIP of Linux v1.0.
*/
@@ -52,7 +45,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.2 gniibe@mri.co.jp\n";
+static const char *version = "NET3 PLIP version 2.2-parport gniibe@mri.co.jp\n";
/*
Sources:
@@ -87,7 +80,7 @@ static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n";
*/
#include <linux/module.h>
-
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -102,6 +95,7 @@ static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n";
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/lp.h>
+#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -114,6 +108,11 @@ static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n";
#include <asm/irq.h>
#include <asm/byteorder.h>
+#include <linux/parport.h>
+
+/* Maximum number of devices to support. */
+#define PLIP_MAX 8
+
/* Use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 1
@@ -143,14 +142,15 @@ static void plip_bh(struct device *dev);
static void plip_interrupt(int irq, void *dev_id, 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_rebuild_header(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 struct net_device_stats *plip_get_stats(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);
+static int plip_preempt(void *handle);
+static int plip_wakeup(void *handle);
enum plip_connection_state {
PLIP_CN_NONE=0,
@@ -188,7 +188,7 @@ struct plip_local {
unsigned char lsb;
#else
#error "Please fix the endianness defines in <asm/byteorder.h>"
-#endif
+#endif
} b;
unsigned short h;
} length;
@@ -199,67 +199,52 @@ struct plip_local {
};
struct net_local {
- struct enet_statistics enet_stats;
+ struct net_device_stats enet_stats;
struct tq_struct immediate;
struct tq_struct deferred;
struct plip_local snd_data;
struct plip_local rcv_data;
+ struct ppd *pardev;
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);
+ int is_deferred;
+ int port_owner;
+ int should_relinquish;
+ int (*orig_rebuild_header)(struct sk_buff *skb);
};
/* Entry point of PLIP driver.
- Probe the hardware, and register/initialize the driver. */
-int
-plip_init(struct device *dev)
+ Probe the hardware, and register/initialize the driver.
+
+ PLIP is rather weird, because of the way it interacts with the parport
+ system. It is _not_ initialised from Space.c. Instead, plip_init()
+ is called, and that function makes up a "struct device" for each port, and
+ then calls us here.
+
+ */
+__initfunc(int
+plip_init_dev(struct device *dev, struct parport *pb))
{
struct net_local *nl;
- int iosize = (PAR_DATA(dev) == 0x3bc) ? 3 : 8;
+ struct ppd *pardev;
- /* Check region before the probe */
- if (check_region(PAR_DATA(dev), iosize) < 0)
- return -ENODEV;
+ dev->irq = pb->irq;
+ dev->base_addr = pb->base;
- /* Check that there is something at base_addr. */
- outb(0, PAR_DATA(dev));
- udelay(1000);
- if (inb(PAR_DATA(dev)) != 0)
+ if (pb->irq == -1) {
+ printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name);
return -ENODEV;
-
- 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
- /* dev->irq==0 means autoprobe, but we don't try to do so
- with module. We can change it by ifconfig */
-#else
- 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 (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);
}
+
+ pardev = parport_register_device(pb, dev->name, plip_preempt,
+ plip_wakeup,
+ plip_interrupt, PARPORT_DEV_LURK, dev);
- request_region(PAR_DATA(dev), iosize, dev->name);
+ printk(version);
+ printk("%s: Parallel port at %#3lx, using IRQ %d\n", dev->name,
+ dev->base_addr, dev->irq);
/* Fill in the generic fields of the device structure. */
ether_setup(dev);
@@ -278,7 +263,7 @@ plip_init(struct device *dev)
dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
if (dev->priv == NULL) {
printk(KERN_ERR "%s: out of memory\n", dev->name);
- release_region(PAR_DATA(dev), iosize);
+ parport_unregister_device(pardev);
return -ENOMEM;
}
memset(dev->priv, 0, sizeof(struct net_local));
@@ -286,6 +271,9 @@ plip_init(struct device *dev)
nl->orig_rebuild_header = dev->rebuild_header;
dev->rebuild_header = plip_rebuild_header;
+ nl->pardev = pardev;
+
+ nl->port_owner = 0;
/* Initialize constants */
nl->trigger = PLIP_TRIGGER_WAIT;
@@ -415,7 +403,6 @@ plip_bh_timeout_error(struct device *dev, struct net_local *nl,
}
rcv->state = PLIP_PK_DONE;
if (rcv->skb) {
- rcv->skb->free = 1;
kfree_skb(rcv->skb, FREE_READ);
rcv->skb = NULL;
}
@@ -556,7 +543,7 @@ plip_receive_packet(struct device *dev, struct net_local *nl,
case PLIP_PK_DATA:
lbuf = rcv->skb->data;
do
- if (plip_receive(nibble_timeout, status_addr,
+ if (plip_receive(nibble_timeout, status_addr,
&rcv->nibble, &lbuf[rcv->byte]))
return TIMEOUT;
while (++rcv->byte < rcv->length.h);
@@ -608,7 +595,7 @@ plip_receive_packet(struct device *dev, struct net_local *nl,
return OK;
}
-/* PLIP_SEND --- send a byte (two nibbles)
+/* 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,
@@ -628,7 +615,7 @@ plip_send(unsigned short nibble_timeout, unsigned short data_addr,
data_addr++;
while (1) {
c0 = inb(data_addr);
- if ((c0 & 0x80) == 0)
+ if ((c0 & 0x80) == 0)
break;
if (--cx == 0)
return TIMEOUT;
@@ -773,6 +760,10 @@ plip_connection_close(struct device *dev, struct net_local *nl,
mark_bh(NET_BH);
}
sti();
+ if (nl->should_relinquish) {
+ nl->should_relinquish = nl->port_owner = 0;
+ parport_release(nl->pardev);
+ }
return OK;
}
@@ -788,6 +779,7 @@ plip_error(struct device *dev, struct net_local *nl,
if (net_debug > 2)
printk("%s: reset interface.\n", dev->name);
nl->connection = PLIP_CN_NONE;
+ nl->should_relinquish = 0;
dev->tbusy = 0;
dev->interrupt = 0;
outb(PAR_INTR_ON, PAR_CONTROL(dev));
@@ -806,8 +798,8 @@ static void
plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
struct device *dev = (struct device *) irq2dev_map[irq];
- struct net_local *nl = (struct net_local *)dev->priv;
- struct plip_local *rcv = &nl->rcv_data;
+ struct net_local *nl;
+ struct plip_local *rcv;
unsigned char c0;
if (dev == NULL) {
@@ -815,6 +807,9 @@ plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
return;
}
+ nl = (struct net_local *)dev->priv;
+ rcv = &nl->rcv_data;
+
if (dev->interrupt)
return;
@@ -857,17 +852,21 @@ plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
/* 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)
+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 *)buff;
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
int i;
if ((dev->flags & IFF_NOARP)==0)
- return nl->orig_rebuild_header(buff, dev, dst, skb);
+ return nl->orig_rebuild_header(skb);
- if (eth->h_proto != htons(ETH_P_IP)) {
+ if (eth->h_proto != __constant_htons(ETH_P_IP)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ && eth->h_proto != __constant_htons(ETH_P_IPV6)
+#endif
+ ) {
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;
@@ -875,7 +874,15 @@ plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
for (i=0; i < ETH_ALEN - sizeof(u32); i++)
eth->h_dest[i] = 0xfc;
+#if 0
*(u32 *)(eth->h_dest+i) = dst;
+#else
+ /* Do not want to include net/route.h here.
+ * In any case, it is TOP of silliness to emulate
+ * hardware addresses on PtP link. --ANK
+ */
+ *(u32 *)(eth->h_dest+i) = 0;
+#endif
return 0;
}
@@ -888,6 +895,13 @@ plip_tx_packet(struct sk_buff *skb, struct device *dev)
if (dev->tbusy)
return 1;
+ /* We may need to grab the bus */
+ if (!nl->port_owner) {
+ if (parport_claim(nl->pardev))
+ return 1;
+ nl->port_owner = 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. */
@@ -938,18 +952,13 @@ plip_open(struct device *dev)
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;
+ /* Grab the port */
+ if (!nl->port_owner) {
+ if (parport_claim(nl->pardev)) return -EAGAIN;
+ nl->port_owner = 1;
}
- cli();
- if (request_irq(dev->irq , plip_interrupt, 0, dev->name, NULL) != 0) {
- sti();
- printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
- return -EAGAIN;
- }
- irq2dev_map[dev->irq] = dev;
- sti();
+
+ nl->should_relinquish = 0;
/* Clear the data port. */
outb (0x00, PAR_DATA(dev));
@@ -966,11 +975,14 @@ plip_open(struct device *dev)
/* Fill in the MAC-level header. */
for (i=0; i < ETH_ALEN - sizeof(u32); i++)
dev->dev_addr[i] = 0xfc;
+ /* Ugh, this is like death. */
*(u32 *)(dev->dev_addr+i) = dev->pa_addr;
dev->interrupt = 0;
dev->start = 1;
dev->tbusy = 0;
+ irq2dev_map[dev->irq] = dev;
+
MOD_INC_USE_COUNT;
return 0;
}
@@ -986,12 +998,17 @@ plip_close(struct device *dev)
dev->tbusy = 1;
dev->start = 0;
cli();
- free_irq(dev->irq, NULL);
irq2dev_map[dev->irq] = NULL;
- nl->is_deferred = 0;
- nl->connection = PLIP_CN_NONE;
sti();
+#ifdef NOTDEF
outb(0x00, PAR_DATA(dev));
+#endif
+ nl->is_deferred = 0;
+ nl->connection = PLIP_CN_NONE;
+ if (nl->port_owner) {
+ parport_release(nl->pardev);
+ nl->port_owner = 0;
+ }
snd->state = PLIP_PK_DONE;
if (snd->skb) {
@@ -1000,22 +1017,69 @@ plip_close(struct device *dev)
}
rcv->state = PLIP_PK_DONE;
if (rcv->skb) {
- rcv->skb->free = 1;
kfree_skb(rcv->skb, FREE_READ);
rcv->skb = NULL;
}
+#ifdef NOTDEF
/* Reset. */
outb(0x00, PAR_CONTROL(dev));
+#endif
MOD_DEC_USE_COUNT;
return 0;
}
-static struct enet_statistics *
+static int
+plip_preempt(void *handle)
+{
+ struct device *dev = (struct device *)handle;
+ struct net_local *nl = (struct net_local *)dev->priv;
+
+ /* Stand our ground if a datagram is on the wire */
+ if (nl->connection != PLIP_CN_NONE) {
+ nl->should_relinquish = 1;
+ return 1;
+ }
+
+ nl->port_owner = 0; /* Remember that we released the bus */
+ return 0;
+}
+
+static int
+plip_wakeup(void *handle)
+{
+ struct device *dev = (struct device *)handle;
+ struct net_local *nl = (struct net_local *)dev->priv;
+
+ if (nl->port_owner) {
+ /* Why are we being woken up? */
+ printk(KERN_DEBUG "%s: why am I being woken up?\n", dev->name);
+ if (!parport_claim(nl->pardev))
+ /* bus_owner is already set (but why?) */
+ printk(KERN_DEBUG "%s: I'm broken.\n", dev->name);
+ else
+ return 1;
+ }
+
+ if (!(dev->flags & IFF_UP))
+ /* Don't need the port when the interface is down */
+ return 1;
+
+ if (!parport_claim(nl->pardev)) {
+ nl->port_owner = 1;
+ irq2dev_map[dev->irq] = dev;
+ /* Clear the data port. */
+ outb (0x00, PAR_DATA(dev));
+ }
+
+ return 0;
+}
+
+static struct net_device_stats *
plip_get_stats(struct device *dev)
{
struct net_local *nl = (struct net_local *)dev->priv;
- struct enet_statistics *r = &nl->enet_stats;
+ struct net_device_stats *r = &nl->enet_stats;
return r;
}
@@ -1040,7 +1104,7 @@ 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;
@@ -1056,88 +1120,133 @@ plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
return 0;
}
-#ifdef MODULE
-static int io[] = {0, 0, 0};
-static int irq[] = {0, 0, 0};
-
-static struct device dev_plip[] = {
- {
- "plip0",
- 0, 0, 0, 0, /* memory */
- 0x3BC, 5, /* base, irq */
- 0, 0, 0, NULL, plip_init
- },
- {
- "plip1",
- 0, 0, 0, 0, /* memory */
- 0x378, 7, /* base, irq */
- 0, 0, 0, NULL, plip_init
- },
- {
- "plip2",
- 0, 0, 0, 0, /* memory */
- 0x278, 2, /* base, irq */
- 0, 0, 0, NULL, plip_init
- }
-};
+static int parport[PLIP_MAX] = { -1, };
+static int timid = 0;
+
+MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i");
+MODULE_PARM(timid, "1i");
-int
-init_module(void)
+static struct device *dev_plip[PLIP_MAX] = { NULL, };
+
+#ifdef MODULE
+void
+cleanup_module(void)
{
- int no_parameters=1;
- int devices=0;
int i;
- /* When user feeds parameters, use them */
- for (i=0; i < 3; i++) {
- int specified=0;
-
- if (io[i] != 0) {
- dev_plip[i].base_addr = io[i];
- specified++;
- }
- if (irq[i] != 0) {
- dev_plip[i].irq = irq[i];
- specified++;
- }
- if (specified) {
- if (register_netdev(&dev_plip[i]) != 0) {
- printk(KERN_INFO "plip%d: Not found\n", i);
- return -EIO;
- }
- no_parameters = 0;
+ for (i=0; i < PLIP_MAX; i++) {
+ if (dev_plip[i]) {
+ struct net_local *nl =
+ (struct net_local *)dev_plip[i]->priv;
+ unregister_netdev(dev_plip[i]);
+ if (nl->port_owner)
+ parport_release(nl->pardev);
+ parport_unregister_device(nl->pardev);
+ kfree(dev_plip[i]->priv);
+ kfree(dev_plip[i]->name);
+ kfree(dev_plip[i]);
+ dev_plip[i] = NULL;
}
}
- if (!no_parameters)
- return 0;
+}
+
+#define plip_init init_module
+
+#else /* !MODULE */
- /* No parameters. Default action is probing all interfaces. */
- for (i=0; i < 3; i++) {
- if (register_netdev(&dev_plip[i]) == 0)
- devices++;
+static int parport_ptr = 0;
+
+void plip_setup(char *str, int *ints)
+{
+ /* Ugh. */
+ if (!strncmp(str, "parport", 7)) {
+ int n = simple_strtoul(str+7, NULL, 10);
+ if (parport_ptr < PLIP_MAX)
+ parport[parport_ptr++] = n;
+ else
+ printk(KERN_INFO "plip: too many ports, %s ignored.\n",
+ str);
+ } else if (!strcmp(str, "timid")) {
+ timid = 1;
+ } else {
+ if (ints[0] == 0 || ints[1] == 0) {
+ /* disable driver on "parport=" or "parport=0" */
+ parport[0] = -2;
+ } else {
+ printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n",
+ ints[1]);
+ }
}
- if (devices == 0) {
- printk(KERN_INFO "plip: no interfaces found\n");
- return -EIO;
+}
+
+#endif /* MODULE */
+
+static int inline
+plip_searchfor(int list[], int a)
+{
+ int i;
+ for (i = 0; i < 3 && list[i] != -1; i++) {
+ if (list[i] == a) return 1;
}
return 0;
}
-void
-cleanup_module(void)
+__initfunc(int
+plip_init(void))
{
- int i;
+ struct parport *pb = parport_enumerate();
+ int devices=0;
+ int i=0;
+
+ if (parport[0] == -2)
+ return 0;
- for (i=0; i < 3; i++) {
- if (dev_plip[i].priv) {
- unregister_netdev(&dev_plip[i]);
- release_region(PAR_DATA(&dev_plip[i]), (PAR_DATA(&dev_plip[i]) == 0x3bc)? 3 : 8);
- kfree_s(dev_plip[i].priv, sizeof(struct net_local));
- dev_plip[i].priv = NULL;
+ if (parport[0] != -1 && timid) {
+ printk(KERN_WARNING "plip: warning, ignoring `timid' since specific ports given.\n");
+ timid = 0;
+ }
+
+ /* When user feeds parameters, use them */
+ while (pb) {
+ if ((parport[0] == -1 && (!timid || !pb->devices)) ||
+ plip_searchfor(parport, i)) {
+ if (i == PLIP_MAX) {
+ printk(KERN_ERR "plip: too many devices\n");
+ break;
+ }
+ dev_plip[i] = kmalloc(sizeof(struct device),
+ GFP_KERNEL);
+ if (!dev_plip[i]) {
+ printk(KERN_ERR "plip: memory squeeze\n");
+ break;
+ }
+ memset(dev_plip[i], 0, sizeof(struct device));
+ dev_plip[i]->name =
+ kmalloc(strlen("plipXXX"), GFP_KERNEL);
+ if (!dev_plip[i]->name) {
+ printk(KERN_ERR "plip: memory squeeze.\n");
+ kfree(dev_plip[i]);
+ break;
+ }
+ sprintf(dev_plip[i]->name, "plip%d", i);
+ dev_plip[i]->priv = pb;
+ if (plip_init_dev(dev_plip[i],pb) || register_netdev(dev_plip[i])) {
+ kfree(dev_plip[i]->name);
+ kfree(dev_plip[i]);
+ } else {
+ devices++;
+ }
}
+ i++;
+ pb = pb->next;
+ }
+
+ if (devices == 0) {
+ printk(KERN_INFO "plip: no devices registered\n");
+ return -EIO;
}
+ return 0;
}
-#endif /* MODULE */
/*
* Local variables: