[500] in linux-net channel archive
Modularised WD80x3 driver (patch against 1.2.10)
daemon@ATHENA.MIT.EDU (Stefan Kuehnel)
Wed Jun 14 22:56:53 1995
Date: Wed, 14 Jun 1995 13:47:06 +0200 (MET DST)
From: Stefan Kuehnel <stefan@idt.unit.no>
To: linux-net@vger.rutgers.edu
In-Reply-To: <199506140957.FAA17154@vger.rutgers.edu>
Hi,
I just converted the WD80x3 driver so that it can be used as a module.
I don't know if this is the right way to do it, but as I've seen some
patches in this group (and it is a rather short patch), I post it
here.
It's my first try at Linux kernel/driver programming, so any comments
are very welcome. Most ideas for modularisation are stolen from the
very good drivers of David C. Davies (depca.c, de4x5.c). I haven't
tested it for very long, but I think the new bugs are hidden rather
good :-).
BTW, is the #ifdef HAVE_DEVLIST still sensible? I would have removed
this stuff, but I wasn't sure. Also I'd like to move the request_irq
stuff from wd_probe1 to wd_open, but as it was my first try....
Let the flames come in :-).
Stefan
PS: I'll leave this evening for 1 week/10 days, so I can answer any mail
only after that.
-----------------------------------------------------------------------
Stefan Kuehnel, Herman Krags vei 1-43, N-7035 Trondheim, Norway
phone: home: +47/73889754, office: +47/73591446
email: stefan@idt.unit.no, un06@rz.uni-karlsruhe.de, kuehnel@ira.uka.de
--- linux-1.2.10/drivers/net/Makefile Tue Apr 25 06:27:16 1995
+++ linux/drivers/net/Makefile Tue Jun 13 21:59:29 1995
@@ -33,6 +33,8 @@
ifdef CONFIG_WD80x3
NETDRV_OBJS := $(NETDRV_OBJS) wd.o
CONFIG_8390 = CONFIG_8390
+else
+MODULES := $(MODULES) wd.o
endif
wd.o: wd.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $<
--- linux-1.2.10/drivers/net/wd.c Fri Apr 28 15:56:53 1995
+++ linux/drivers/net/wd.c Wed Jun 14 02:16:55 1995
@@ -15,11 +15,22 @@
This is a driver for WD8003 and WD8013 "compatible" ethercards.
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+
+ Modularised 06/95 Stefan Kuehnel (stefan@idt.unit.no)
*/
static char *version =
"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif /* MODULE */
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -29,12 +40,6 @@
#include <linux/netdevice.h>
#include "8390.h"
-extern struct device *init_etherdev(struct device *dev, int sizeof_private,
- unsigned long *mem_startp);
-
-/* A zero-terminated list of I/O addresses to be probed. */
-static unsigned int wd_portlist[] =
-{0x300, 0x280, 0x380, 0x240, 0};
int wd_probe(struct device *dev);
int wd_probe1(struct device *dev, int ioaddr);
@@ -47,6 +52,18 @@
const unsigned char *buf, const start_page);
static int wd_close_card(struct device *dev);
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+
+#else
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int wd_portlist[] =
+{0x300, 0x280, 0x380, 0x240, 0};
+
+#endif /* MODULE */
+
#define WD_START_PG 0x00 /* First page of TX buffer */
#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
@@ -77,25 +94,36 @@
int wd_probe(struct device *dev)
{
- int i;
int base_addr = dev ? dev->base_addr : 0;
+ int status = -ENODEV;
+
+ if (base_addr > 0x1ff) { /* Check a single specified location. */
+ status = wd_probe1(dev, base_addr);
+ } else if (base_addr != 0) { /* Don't probe at all. */
+ status = -ENXIO;
+
+ } else { /* base_addr == 0 => auto_probe */
+#ifdef MODULE
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+#else
+ int i;
- if (base_addr > 0x1ff) /* Check a single specified location. */
- return wd_probe1(dev, base_addr);
- else if (base_addr != 0) /* Don't probe at all. */
- return ENXIO;
-
- for (i = 0; wd_portlist[i]; i++) {
- int ioaddr = wd_portlist[i];
- if (check_region(ioaddr, WD_IO_EXTENT))
- continue;
- if (wd_probe1(dev, ioaddr) == 0)
- return 0;
+ for (i = 0; wd_portlist[i]; i++) {
+ int ioaddr = wd_portlist[i];
+ if (check_region(ioaddr, WD_IO_EXTENT))
+ continue;
+ if (wd_probe1(dev, ioaddr) == 0) {
+ status = 0;
+ break;
+ }
+ }
+#endif /* MODULE */
}
- return ENODEV;
+ return status;
}
-#endif
+#endif /* HAVE_DEVLIST */
int wd_probe1(struct device *dev, int ioaddr)
{
@@ -110,10 +138,7 @@
if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
|| inb(ioaddr + 9) == 0xff
|| (checksum & 0xff) != 0xFF)
- return ENODEV;
-
- if (dev == NULL)
- dev = init_etherdev(0, sizeof(struct ei_device), 0);
+ return -ENODEV;
printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
for (i = 0; i < 6; i++)
@@ -203,6 +228,7 @@
int reg1 = inb(ioaddr+1);
int reg4 = inb(ioaddr+4);
if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
+#ifndef MODULE
short nic_addr = ioaddr+WD_NIC_OFFSET;
/* We have an old-style ethercard that doesn't report its IRQ
@@ -220,6 +246,7 @@
if (ei_debug > 2)
printk(" autoirq is %d", dev->irq);
+#endif /* MODULE */
if (dev->irq < 2)
dev->irq = word16 ? 10 : 5;
} else
@@ -227,15 +254,35 @@
} else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
dev->irq = 9;
+ /* Allocate priv memory here, so that it can safely free'd */
+ /* Otherwise it would be kmalloc'd in ethdev_init() */
+ /* This is only here because 8390.c is broken. If it allocates */
+ /* the memory, it should free it cleanup_module() */
+ /* (I know it's a small structure but still it can't hurt) */
+ dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
+ if (dev->priv) {
+ struct ei_device *ei_local;
+
+ memset(dev->priv, 0, sizeof(struct ei_device));
+ ei_local = (struct ei_device *)dev->priv;
+#ifndef NO_PINGPONG
+ ei_local->pingpong = 1;
+#endif
+ } else {
+ printk(" insufficient memory\n");
+ return -EAGAIN;
+ }
+
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
- if (request_irq(dev->irq, ei_interrupt, 0, "wd")) {
+ if (request_irq(dev->irq, ei_interrupt, 0, dev->name)) {
printk (" unable to get IRQ %d.\n", dev->irq);
- return EAGAIN;
+ kfree_s(dev->priv, sizeof(struct ei_device));
+ return -EAGAIN;
}
/* OK, were are certain this is going to work. Setup the device. */
- request_region(ioaddr, WD_IO_EXTENT,"wd");
+ request_region(ioaddr, WD_IO_EXTENT, dev->name);
ethdev_init(dev);
ei_status.name = model_name;
@@ -275,6 +322,7 @@
wd_open(struct device *dev)
{
int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ int status;
/* Map in the shared memory. Always set register 0 last to remain
compatible with very old boards. */
@@ -285,7 +333,12 @@
outb(ei_status.reg5, ioaddr+WD_CMDREG5);
outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
- return ei_open(dev);
+ if ((status = ei_open(dev)))
+ return status;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
}
static void
@@ -376,8 +429,52 @@
/* And disable the shared memory. */
outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
+ MOD_DEC_USE_COUNT;
+
return 0;
}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device thisCard = {
+ " ", /* device name inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x280, 5, /* I/O address, IRQ */
+ 0, 0, 0, NULL, wd_probe };
+
+/*
+ * This is a tweak to keep the insmod program happy. It can only
+ * set int values with var=value so we split these out.
+ */
+
+int irq=5; /* EDIT THESE LINE FOR YOUR CONFIGURATION */
+int io=0x280; /* Or use the irq= io= options to insmod */
+
+int
+init_module(void)
+{
+ thisCard.irq=irq;
+ thisCard.base_addr=io;
+ if (register_netdev(&thisCard) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int ioaddr = thisCard.base_addr - WD_NIC_OFFSET;
+
+ free_irq(thisCard.irq);
+ release_region(ioaddr, WD_IO_EXTENT);
+
+ kfree_s(thisCard.priv, sizeof(struct ei_device));
+ thisCard.priv = NULL;
+
+ unregister_netdev(&thisCard);
+}
+#endif /* MODULE */
+
/*