[1262] in linux-net channel archive
Re: Network Drivers as Module
daemon@ATHENA.MIT.EDU (Paul Gortmaker)
Fri Oct 27 12:20:02 1995
From: Paul Gortmaker <gpg109@rsphy4.anu.edu.au>
To: gniibe@mri.co.jp (NIIBE Yutaka)
Date: Thu, 26 Oct 1995 18:34:11 +1000 (EST)
Cc: torvalds@cs.helsinki.fi (Linus Torvalds), linux-net@vger.rutgers.edu
In-Reply-To: <199510250452.NAA00227@megatherium.mri.co.jp> from "NIIBE Yutaka" at Oct 25, 95 01:52:47 pm
From "NIIBE Yutaka" Oct 25, 95 01:52:47 pm
> Currently (as of 1.3.35), most network drivers cannot handle two (or
> more) cards simulteneously as module. That is, we don't use module
> for multi-homed host with same network cards.
First up, I didn't do all the net module stuff that took place around
1.3.17 so it isn't my fault ;-) The ISA cards shouldn't have been
modules anyway, but since they are already, I guess it should be done
right... <sigh>
Unfortunately the two things that usually get overlooked when people
do device driver work is consideration for multiple card systems,
and safe probing techniques.
Anyway here is a fix for the wd80x3 driver. I can now do as follows:
------------------------------------------------------------------
ratbag:~# insmod wd.o io=0x240,0x280,0x300
wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)
eth0: WD80x3 at 0x240, 00 00 C0 8F 53 1C WD8003, IRQ 7, shared memory
at 0xde000-0xdffff.
eth1: WD80x3 at 0x280, 00 00 C0 72 4D 1C WD8003, IRQ 9, shared memory
at 0xd8000-0xd9fff.
eth2: WD80x3 at 0x300, 00 00 C0 4E DC 52 WD8003, IRQ 5, shared memory
at 0xe0000-0xe1fff.
ratbag:~#
------------------------------------------------------------------
If no io arg(s) is/are given, the user is warned against autoprobing
for these devices, and then a *single* autoprobe takes place. To use
multiple devices per insmod, you *have* to specify the i/o bases.
I will do the same for the other 8390 cards, one at a time, when
I get a free moment. Somebody else can do the non-8390 ones. If
somebody uses the following patch as a pattern, it is then just an
editing job. Also, I dscovered that none of the 8390 cards would have
ever worked as modules with the 8390 core compiled in, due to missing
ksyms. That is fixed as well. And I got rid of "loading device %s...",
as each device has its own initialization banner/message anyway.
Paul.
diff -ur linux-1336-oem/drivers/net/net_init.c linux/drivers/net/net_init.c
--- linux-1336-oem/drivers/net/net_init.c Tue Oct 10 22:01:01 1995
+++ linux/drivers/net/net_init.c Thu Oct 26 15:20:28 1995
@@ -247,7 +247,6 @@
for (i = 0; i < MAX_ETH_CARDS; ++i)
if (ethdev_index[i] == NULL) {
sprintf(dev->name, "eth%d", i);
- printk("loading device '%s'...\n", dev->name);
ethdev_index[i] = dev;
break;
}
diff -ur linux-1336-oem/drivers/net/wd.c linux/drivers/net/wd.c
--- linux-1336-oem/drivers/net/wd.c Tue Oct 10 22:03:45 1995
+++ linux/drivers/net/wd.c Thu Oct 26 17:44:44 1995
@@ -15,6 +15,11 @@
This is a driver for WD8003 and WD8013 "compatible" ethercards.
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users
+
*/
static const char *version =
@@ -110,6 +115,7 @@
int ancient = 0; /* An old card without config registers. */
int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
const char *model_name;
+ static unsigned version_printed = 0;
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
@@ -121,6 +127,9 @@
if (dev == NULL)
dev = init_etherdev(0, sizeof(struct ei_device));
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
for (i = 0; i < 6; i++)
printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
@@ -257,8 +266,6 @@
printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
model_name, dev->irq, dev->mem_start, dev->mem_end-1);
- if (ei_debug > 0)
- printk(version);
ei_status.reset_8390 = &wd_reset_8390;
ei_status.block_input = &wd_block_input;
@@ -408,44 +415,61 @@
#ifdef MODULE
+#define MAX_WD_MODS 4 /* Max number of wd modules allowed */
+#define NAMELEN 9 /* # of chars for storing dev->name */
char kernel_version[] = UTS_RELEASE;
-static char devicename[9] = { 0, };
-static struct device dev_wd80x3 = {
- devicename, /* device name is inserted by linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0, 0,
- 0, 0, 0, NULL, wd_probe };
-
-int io = 0x300;
-int irq = 0;
-int mem = 0;
-
-int init_module(void)
+static char namelist[NAMELEN * MAX_WD_MODS] = { 0, };
+static struct device dev_wd80x3[MAX_WD_MODS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_WD_MODS] = { 0, };
+static int irq[MAX_WD_MODS] = { 0, };
+static int mem[MAX_WD_MODS] = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
{
- if (io == 0)
- printk("wd: You should not use auto-probing with insmod!\n");
- dev_wd80x3.base_addr = io;
- dev_wd80x3.irq = irq;
- dev_wd80x3.mem_start = mem;
- if (register_netdev(&dev_wd80x3) != 0)
- return -EIO;
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) {
+ dev_wd80x3[this_dev].name = namelist+(NAMELEN*this_dev);
+ dev_wd80x3[this_dev].irq = irq[this_dev];
+ dev_wd80x3[this_dev].base_addr = io[this_dev];
+ dev_wd80x3[this_dev].mem_start = mem[this_dev];
+ dev_wd80x3[this_dev].init = wd_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(&dev_wd80x3[this_dev]) != 0) {
+ printk(KERN_WARNING "modules: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+ return -EIO;
+ }
+ }
+
return 0;
}
void
cleanup_module(void)
{
- if (MOD_IN_USE)
- printk("wd80x3: device busy, remove delayed\n");
- else
- {
- int ioaddr = dev_wd80x3.base_addr - WD_NIC_OFFSET;
+ int this_dev;
- unregister_netdev(&dev_wd80x3);
-
- /* If we don't do this, we can't re-insmod it later. */
- free_irq(dev_wd80x3.irq);
- release_region(ioaddr, WD_IO_EXTENT);
+ for (this_dev = 0; this_dev < MAX_WD_MODS; this_dev++) {
+ if (dev_wd80x3[this_dev].priv != NULL) {
+ int ioaddr = dev_wd80x3[this_dev].base_addr - WD_NIC_OFFSET;
+ unregister_netdev(&dev_wd80x3[this_dev]);
+ free_irq(dev_wd80x3[this_dev].irq);
+ release_region(ioaddr, WD_IO_EXTENT);
+ }
}
}
#endif /* MODULE */
diff -ur linux-1336-oem/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-1336-oem/kernel/ksyms.c Fri Oct 20 21:33:10 1995
+++ linux/kernel/ksyms.c Thu Oct 26 14:30:16 1995
@@ -78,6 +78,13 @@
extern void *sys_call_table;
+#if defined(CONFIG_ULTRA) || defined(CONFIG_WD80x3) || \
+ defined(CONFIG_EL2) || defined(CONFIG_NE2000) || \
+ defined(CONFIG_E2100) || defined(CONFIG_HPLAN_PLUS) || \
+ defined(CONFIG_HPLAN) || defined(CONFIG_AC3200)
+#include "../drivers/net/8390.h"
+#endif
+
#ifdef CONFIG_SCSI
#include "../drivers/scsi/scsi.h"
#include "../drivers/scsi/scsi_ioctl.h"
@@ -329,6 +336,17 @@
X(arp_send),
#ifdef CONFIG_IP_FORWARD
X(ip_forward),
+#endif
+#if defined(CONFIG_ULTRA) || defined(CONFIG_WD80x3) || \
+ defined(CONFIG_EL2) || defined(CONFIG_NE2000) || \
+ defined(CONFIG_E2100) || defined(CONFIG_HPLAN_PLUS) || \
+ defined(CONFIG_HPLAN) || defined(CONFIG_AC3200)
+ /* If 8390 NIC support is built in, we will need these. */
+ X(ei_open),
+ X(ei_debug),
+ X(ei_interrupt),
+ X(ethdev_init),
+ X(NS8390_init),
#endif
#if defined(CONFIG_PPP) || defined(CONFIG_SLIP)
/* VJ header compression */
}