## Automatically generated incremental diff
## From:   linux-2.4.3-pre6
## To:     linux-2.4.3-pre7
## Robot:  $Id: make-incremental-diff,v 1.8 2001/12/04 06:05:04 hpa Exp $

diff -urN linux-2.4.3-pre6/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.4.3-pre6/Documentation/Configure.help	Mon Dec  3 22:32:54 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 22:33:04 2001
@@ -7474,6 +7474,18 @@
   location). You also want to check out the PCMCIA-HOWTO, available
   from http://www.linuxdoc.org/docs.html#howto .
 
+Hermes (AT&T/Lucent/Orinoco/3com) wireless support
+CONFIG_PCMCIA_HERMES
+  A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
+  as the Lucent/Orinoco cards and the Cabletron RoamAbout. It should
+  also be usable on Prism II based cards such as the Farallon Skyline
+  and the 3Com AirConnect.
+
+  To use your PC-cards, you will need supporting software from David
+  Hinds' pcmcia-cs package (see the file Documentation/Changes for
+  location). You also want to check out the PCMCIA-HOWTO, available
+  from http://www.linuxdoc.org/docs.html#howto .
+
 Aviator/Raytheon 2.4MHz wireless support
 CONFIG_PCMCIA_RAYCS
   Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
diff -urN linux-2.4.3-pre6/Documentation/SAK.txt linux/Documentation/SAK.txt
--- linux-2.4.3-pre6/Documentation/SAK.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/SAK.txt	Mon Dec  3 22:33:04 2001
@@ -0,0 +1,88 @@
+Linux 2.4.2 Secure Attention Key (SAK) handling
+18 March 2001, Andrew Morton <andrewm@uow.edu.au>
+
+An operating system's Secure Attention Key is a security tool which is
+provided as protection against trojan password capturing programs.  It
+is an undefeatable way of killing all programs which could be
+masquerading as login applications.  Users need to be taught to enter
+this key sequence before they log in to the system.
+
+From the PC keyboard, Linux has two similar but different ways of
+providing SAK.  One is the ALT-SYSRQ-K sequence.  You shouldn't use
+this sequence.  It is only available if the kernel was compiled with
+sysrq support.
+
+The proper way of generating a SAK is to define the key sequence using
+`loadkeys'.  This will work whether or not sysrq support is compiled
+into the kernel.
+
+SAK works correctly when the keyboard is in raw mode.  This means that
+once defined, SAK will kill a running X server.  If the system is in
+run level 5, the X server will restart.  This is what you want to
+happen.
+
+What key sequence should you use? Well, CTRL-ALT-DEL is used to reboot
+the machine.  CTRL-ALT-BACKSPACE is magical to the X server.  We'll
+choose CTRL-ALT-PAUSE.
+
+In your rc.sysinit (or rc.local) file, add the command
+
+	echo "control alt keycode 101 = SAK" | /bin/loadkeys
+
+And that's it!  Only the superuser may reprogram the SAK key.
+
+
+NOTES
+=====
+
+1: Linux SAK is said to be not a "true SAK" as is required by
+   systems which implement C2 level security.  This author does not
+   know why.
+
+
+2: On the PC keyboard, SAK kills all applications which have
+   /dev/console opened.
+
+   Unfortunately this includes a number of things which you don't
+   actually want killed.  This is because these appliccaitons are
+   incorrectly holding /dev/console open.  Be sure to complain to your
+   Linux distributor about this!
+
+   You can identify processes which will be killed by SAK with the
+   command
+
+	# ls -l /proc/[0-9]*/fd/* | grep console
+	l-wx------    1 root     root           64 Mar 18 00:46 /proc/579/fd/0 -> /dev/console
+
+   Then:
+
+	# ps aux|grep 579
+	root       579  0.0  0.1  1088  436 ?        S    00:43   0:00 gpm -t ps/2
+
+   So `gpm' will be killed by SAK.  This is a bug in gpm.  It should
+   be closing standard input.  You can work around this by finding the
+   initscript which launches gpm and changing it thusly:
+
+   Old:
+
+	daemon gpm
+
+   New:
+
+	daemon gpm < /dev/null
+
+   Vixie cron also seems to have this problem, and needs the same treatment.
+
+   Also, one prominent Linux distribution has the following three
+   lines in its rc.sysinit and rc scripts:
+
+	exec 3<&0
+	exec 4>&1
+	exec 5>&2
+
+   These commands cause *all* daemons which are launched by the
+   initscripts to have file descriptors 3, 4 and 5 attached to
+   /dev/console.  So SAK kills them all.  A workaround is to simply
+   delete these lines, but this may cause system management
+   applications to malfunction - test everything well.
+
diff -urN linux-2.4.3-pre6/Documentation/sysrq.txt linux/Documentation/sysrq.txt
--- linux-2.4.3-pre6/Documentation/sysrq.txt	Fri Jul 28 12:50:52 2000
+++ linux/Documentation/sysrq.txt	Mon Dec  3 22:33:04 2001
@@ -29,6 +29,8 @@
            You send a BREAK, then within 5 seconds a command key. Sending
            BREAK twice is interpreted as a normal BREAK.
 
+On Mac   - Press 'Keypad+-F13-<command key>'
+
 On other - If you know of the key combos for other architectures, please
            let me know so I can add them to this section. 
 
diff -urN linux-2.4.3-pre6/Makefile linux/Makefile
--- linux-2.4.3-pre6/Makefile	Mon Dec  3 22:32:54 2001
+++ linux/Makefile	Mon Dec  3 22:33:04 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 3
-EXTRAVERSION =-pre6
+EXTRAVERSION =-pre7
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.3-pre6/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.4.3-pre6/arch/i386/defconfig	Mon Dec  3 22:32:54 2001
+++ linux/arch/i386/defconfig	Mon Dec  3 22:33:04 2001
@@ -434,6 +434,7 @@
 # CONFIG_PCMCIA_XIRTULIP is not set
 CONFIG_NET_PCMCIA_RADIO=y
 CONFIG_PCMCIA_RAYCS=y
+# CONFIG_PCMCIA_HERMES is not set
 # CONFIG_PCMCIA_NETWAVE is not set
 # CONFIG_PCMCIA_WAVELAN is not set
 # CONFIG_AIRONET4500_CS is not set
diff -urN linux-2.4.3-pre6/arch/ia64/ia32/binfmt_elf32.c linux/arch/ia64/ia32/binfmt_elf32.c
--- linux-2.4.3-pre6/arch/ia64/ia32/binfmt_elf32.c	Mon Dec  3 22:32:54 2001
+++ linux/arch/ia64/ia32/binfmt_elf32.c	Mon Dec  3 22:33:04 2001
@@ -204,7 +204,6 @@
 
 	for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
 		if (bprm->page[i]) {
-			current->mm->rss++;
 			put_dirty_page(current,bprm->page[i],stack_base);
 		}
 		stack_base += PAGE_SIZE;
diff -urN linux-2.4.3-pre6/arch/s390x/kernel/exec32.c linux/arch/s390x/kernel/exec32.c
--- linux-2.4.3-pre6/arch/s390x/kernel/exec32.c	Mon Dec  3 22:32:55 2001
+++ linux/arch/s390x/kernel/exec32.c	Mon Dec  3 22:33:04 2001
@@ -73,7 +73,6 @@
 		struct page *page = bprm->page[i];
 		if (page) {
 			bprm->page[i] = NULL;
-			current->mm->rss++;
 			put_dirty_page(current,page,stack_base);
 		}
 		stack_base += PAGE_SIZE;
diff -urN linux-2.4.3-pre6/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-2.4.3-pre6/drivers/block/ll_rw_blk.c	Mon Dec  3 22:32:55 2001
+++ linux/drivers/block/ll_rw_blk.c	Mon Dec  3 22:33:04 2001
@@ -954,6 +954,8 @@
  */
 void submit_bh(int rw, struct buffer_head * bh)
 {
+	int count = bh->b_size >> 9;
+
 	if (!test_bit(BH_Lock, &bh->b_state))
 		BUG();
 
@@ -964,16 +966,16 @@
 	 * further remap this.
 	 */
 	bh->b_rdev = bh->b_dev;
-	bh->b_rsector = bh->b_blocknr * (bh->b_size >> 9);
+	bh->b_rsector = bh->b_blocknr * count;
 
 	generic_make_request(rw, bh);
 
 	switch (rw) {
 		case WRITE:
-			kstat.pgpgout++;
+			kstat.pgpgout += count;
 			break;
 		default:
-			kstat.pgpgin++;
+			kstat.pgpgin += count;
 			break;
 	}
 }
diff -urN linux-2.4.3-pre6/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- linux-2.4.3-pre6/drivers/cdrom/cdrom.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/cdrom/cdrom.c	Mon Dec  3 22:33:04 2001
@@ -1171,42 +1171,50 @@
 
 static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
 {
-	int ret, i;
-	u_char buf[4 + 4 * 20], *base;
+	unsigned char buf[20], *base;
 	struct dvd_layer *layer;
 	struct cdrom_generic_command cgc;
 	struct cdrom_device_ops *cdo = cdi->ops;
+	int ret, layer_num = s->physical.layer_num;
+
+	if (layer_num >= DVD_LAYERS)
+		return -EINVAL;
 
 	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
 	cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
-	cgc.cmd[6] = s->physical.layer_num;
+	cgc.cmd[6] = layer_num;
 	cgc.cmd[7] = s->type;
 	cgc.cmd[9] = cgc.buflen & 0xff;
 
+	/*
+	 * refrain from reporting errors on non-existing layers (mainly)
+	 */
+	cgc.quiet = 1;
+
 	if ((ret = cdo->generic_packet(cdi, &cgc)))
 		return ret;
 
 	base = &buf[4];
-	layer = &s->physical.layer[0];
+	layer = &s->physical.layer[layer_num];
 
-	/* place the data... really ugly, but at least we won't have to
-	   worry about endianess in userspace or here. */
-	for (i = 0; i < 4; ++i, base += 20, ++layer) {
-		memset(layer, 0, sizeof(*layer));
-		layer->book_version = base[0] & 0xf;
-		layer->book_type = base[0] >> 4;
-		layer->min_rate = base[1] & 0xf;
-		layer->disc_size = base[1] >> 4;
-		layer->layer_type = base[2] & 0xf;
-		layer->track_path = (base[2] >> 4) & 1;
-		layer->nlayers = (base[2] >> 5) & 3;
-		layer->track_density = base[3] & 0xf;
-		layer->linear_density = base[3] >> 4;
-		layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
-		layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
-		layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
-		layer->bca = base[16] >> 7;
-	}
+	/*
+	 * place the data... really ugly, but at least we won't have to
+	 * worry about endianess in userspace.
+	 */
+	memset(layer, 0, sizeof(*layer));
+	layer->book_version = base[0] & 0xf;
+	layer->book_type = base[0] >> 4;
+	layer->min_rate = base[1] & 0xf;
+	layer->disc_size = base[1] >> 4;
+	layer->layer_type = base[2] & 0xf;
+	layer->track_path = (base[2] >> 4) & 1;
+	layer->nlayers = (base[2] >> 5) & 3;
+	layer->track_density = base[3] & 0xf;
+	layer->linear_density = base[3] >> 4;
+	layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
+	layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
+	layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
+	layer->bca = base[16] >> 7;
 
 	return 0;
 }
@@ -1985,7 +1993,7 @@
 		}
 	case CDROMREADAUDIO: {
 		struct cdrom_read_audio ra;
-		int lba;
+		int lba, nr;
 
 		IOCTL_IN(arg, struct cdrom_read_audio, ra);
 
@@ -2002,7 +2010,19 @@
 		if (lba < 0 || ra.nframes <= 0)
 			return -EINVAL;
 
-		if ((cgc.buffer = (char *) kmalloc(CD_FRAMESIZE_RAW, GFP_KERNEL)) == NULL)
+		/*
+		 * start with will ra.nframes size, back down if alloc fails
+		 */
+		nr = ra.nframes;
+		do {
+			cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL);
+			if (cgc.buffer)
+				break;
+
+			nr >>= 1;
+		} while (nr);
+
+		if (!nr)
 			return -ENOMEM;
 
 		if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW)) {
@@ -2011,12 +2031,16 @@
 		}
 		cgc.data_direction = CGC_DATA_READ;
 		while (ra.nframes > 0) {
-			ret = cdrom_read_block(cdi, &cgc, lba, 1, 1, CD_FRAMESIZE_RAW);
-			if (ret) break;
+			if (nr > ra.nframes)
+				nr = ra.nframes;
+
+			ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
+			if (ret)
+				break;
 			__copy_to_user(ra.buf, cgc.buffer, CD_FRAMESIZE_RAW);
-			ra.buf += CD_FRAMESIZE_RAW;
-			ra.nframes--;
-			lba++;
+			ra.buf += CD_FRAMESIZE_RAW * nr;
+			ra.nframes -= nr;
+			lba += nr;
 		}
 		kfree(cgc.buffer);
 		return ret;
diff -urN linux-2.4.3-pre6/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- linux-2.4.3-pre6/drivers/char/tty_io.c	Tue Feb 13 13:15:04 2001
+++ linux/drivers/char/tty_io.c	Mon Dec  3 22:33:04 2001
@@ -60,6 +60,9 @@
  *
  * Reduced memory usage for older ARM systems
  *      -- Russell King <rmk@arm.linux.org.uk>
+ *
+ * Move do_SAK() into process context.  Less stack use in devfs functions.
+ * alloc_tty_struct() always uses kmalloc() -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
  */
 
 #include <linux/config.h>
@@ -159,26 +162,19 @@
 #define MAX(a,b)	((a) < (b) ? (b) : (a))
 #endif
 
-static inline struct tty_struct *alloc_tty_struct(void)
+static struct tty_struct *alloc_tty_struct(void)
 {
 	struct tty_struct *tty;
 
-	if (PAGE_SIZE > 8192) {
-		tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
-		if (tty)
-			memset(tty, 0, sizeof(struct tty_struct));
-	} else
-		tty = (struct tty_struct *)get_zeroed_page(GFP_KERNEL);
-
+	tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
+	if (tty)
+		memset(tty, 0, sizeof(struct tty_struct));
 	return tty;
 }
 
 static inline void free_tty_struct(struct tty_struct *tty)
 {
-	if (PAGE_SIZE > 8192)
-		kfree(tty);
-	else
-		free_page((unsigned long) tty);
+	kfree(tty);
 }
 
 /*
@@ -1814,12 +1810,16 @@
  * Now, if it would be correct ;-/ The current code has a nasty hole -
  * it doesn't catch files in flight. We may send the descriptor to ourselves
  * via AF_UNIX socket, close it and later fetch from socket. FIXME.
+ *
+ * Nasty bug: do_SAK is being called in interrupt context.  This can
+ * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
  */
-void do_SAK( struct tty_struct *tty)
+static void __do_SAK(void *arg)
 {
 #ifdef TTY_SOFT_SAK
 	tty_hangup(tty);
 #else
+	struct tty_struct *tty = arg;
 	struct task_struct *p;
 	int session;
 	int		i;
@@ -1842,7 +1842,6 @@
 		task_lock(p);
 		if (p->files) {
 			read_lock(&p->files->file_lock);
-			/* FIXME: p->files could change */
 			for (i=0; i < p->files->max_fds; i++) {
 				filp = fcheck_files(p->files, i);
 				if (filp && (filp->f_op == &tty_fops) &&
@@ -1860,6 +1859,19 @@
 }
 
 /*
+ * The tq handling here is a little racy - tty->SAK_tq may already be queued.
+ * But there's no mechanism to fix that without futzing with tqueue_lock.
+ * Fortunately we don't need to worry, because if ->SAK_tq is already queued,
+ * the values which we write to it will be identical to the values which it
+ * already has. --akpm
+ */
+void do_SAK(struct tty_struct *tty)
+{
+	PREPARE_TQUEUE(&tty->SAK_tq, __do_SAK, tty);
+	schedule_task(&tty->SAK_tq);
+}
+
+/*
  * This routine is called out of the software interrupt to flush data
  * from the flip buffer to the line discipline.
  */
@@ -1973,6 +1985,7 @@
 	sema_init(&tty->atomic_write, 1);
 	spin_lock_init(&tty->read_lock);
 	INIT_LIST_HEAD(&tty->tty_files);
+	INIT_TQUEUE(&tty->SAK_tq, 0, 0);
 }
 
 /*
@@ -1986,17 +1999,15 @@
 /*
  * Register a tty device described by <driver>, with minor number <minor>.
  */
-void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
-			 unsigned int minor)
+void tty_register_devfs (struct tty_driver *driver, unsigned int flags, unsigned minor)
 {
 #ifdef CONFIG_DEVFS_FS
 	umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
-	struct tty_struct tty;
+	kdev_t device = MKDEV (driver->major, minor);
+	int idx = minor - driver->minor_start;
 	char buf[32];
 
-	tty.driver = *driver;
-	tty.device = MKDEV (driver->major, minor);
-	switch (tty.device) {
+	switch (device) {
 		case TTY_DEV:
 		case PTMX_DEV:
 			mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
@@ -2017,7 +2028,8 @@
 	     (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) )
 		flags |= DEVFS_FL_CURRENT_OWNER;
 #  endif
-	devfs_register (NULL, tty_name (&tty, buf), flags | DEVFS_FL_DEFAULT,
+	sprintf(buf, driver->name, idx + driver->name_base);
+	devfs_register (NULL, buf, flags | DEVFS_FL_DEFAULT,
 			driver->major, minor, mode, &tty_fops, NULL);
 #endif /* CONFIG_DEVFS_FS */
 }
@@ -2026,14 +2038,11 @@
 {
 #ifdef CONFIG_DEVFS_FS
 	void * handle;
-	struct tty_struct tty;
+	int idx = minor - driver->minor_start;
 	char buf[32];
 
-	tty.driver = *driver;
-	tty.device = MKDEV(driver->major, minor);
-	
-	handle = devfs_find_handle (NULL, tty_name (&tty, buf),
-				    driver->major, minor,
+	sprintf(buf, driver->name, idx + driver->name_base);
+	handle = devfs_find_handle (NULL, buf, driver->major, minor,
 				    DEVFS_SPECIAL_CHR, 0);
 	devfs_unregister (handle);
 #endif /* CONFIG_DEVFS_FS */
@@ -2218,9 +2227,6 @@
  */
 void __init tty_init(void)
 {
-	if (sizeof(struct tty_struct) > PAGE_SIZE)
-		panic("size of tty structure > PAGE_SIZE!");
-
 	/*
 	 * dev_tty_driver and dev_console_driver are actually magic
 	 * devices which get redirected at open time.  Nevertheless,
diff -urN linux-2.4.3-pre6/drivers/net/pcmcia/Config.in linux/drivers/net/pcmcia/Config.in
--- linux-2.4.3-pre6/drivers/net/pcmcia/Config.in	Sat Nov 11 18:56:58 2000
+++ linux/drivers/net/pcmcia/Config.in	Mon Dec  3 22:33:04 2001
@@ -26,6 +26,7 @@
    bool '  Pcmcia Wireless LAN' CONFIG_NET_PCMCIA_RADIO
    if [ "$CONFIG_NET_PCMCIA_RADIO" = "y" ]; then
       dep_tristate '    Aviator/Raytheon 2.4MHz wireless support' CONFIG_PCMCIA_RAYCS $CONFIG_PCMCIA
+      dep_tristate '    Hermes (AT&T/Lucent/Orinoco/3com) wireless support' CONFIG_PCMCIA_HERMES $CONFIG_PCMCIA
       dep_tristate '    Xircom Netwave AirSurfer wireless support' CONFIG_PCMCIA_NETWAVE $CONFIG_PCMCIA
       dep_tristate '    AT&T/Lucent Wavelan wireless support' CONFIG_PCMCIA_WAVELAN $CONFIG_PCMCIA
       dep_tristate '    Aironet 4500/4800 PCMCIA support' CONFIG_AIRONET4500_CS $CONFIG_AIRONET4500 $CONFIG_PCMCIA
diff -urN linux-2.4.3-pre6/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile
--- linux-2.4.3-pre6/drivers/net/pcmcia/Makefile	Fri Dec 29 14:07:22 2000
+++ linux/drivers/net/pcmcia/Makefile	Mon Dec  3 22:33:04 2001
@@ -25,6 +25,7 @@
 obj-$(CONFIG_ARCNET_COM20020_CS)+= com20020_cs.o
 
 # 16-bit wireless client drivers
+obj-$(CONFIG_PCMCIA_HERMES)	+= orinoco_cs.o hermes.o
 obj-$(CONFIG_PCMCIA_RAYCS)	+= ray_cs.o
 obj-$(CONFIG_PCMCIA_NETWAVE)	+= netwave_cs.o
 obj-$(CONFIG_PCMCIA_WAVELAN)	+= wavelan_cs.o
diff -urN linux-2.4.3-pre6/drivers/net/pcmcia/hermes.c linux/drivers/net/pcmcia/hermes.c
--- linux-2.4.3-pre6/drivers/net/pcmcia/hermes.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/pcmcia/hermes.c	Mon Dec  3 22:33:04 2001
@@ -0,0 +1,498 @@
+/* hermes.c
+ *
+ * Driver core for the "Hermes" wireless MAC controller, as used in
+ * the Lucent Orinoco and Cabletron RoamAbout cards. It should also
+ * work on the hfa3841 and hfa3842 MAC controller chips used in the
+ * Prism I & II chipsets.
+ *
+ * This is not a complete driver, just low-level access routines for
+ * the MAC controller itself.
+ *
+ * Based on the prism2 driver from Absolute Value Systems' linux-wlan
+ * project, the Linux wvlan_cs driver, Lucent's HCF-Light
+ * (wvlan_hcf.c) library, and the NetBSD wireless driver.
+ *
+ * Copyright (C) 2000, David Gibson, Linuxcare Australia <hermes@gibson.dropbear.id.au>
+ * 
+ * This file distributed under the GPL, version 2.
+ */
+
+static const char *version = "hermes.c: 12 Dec 2000 David Gibson <hermes@gibson.dropbear.id.au>";
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/smp.h>
+#include <asm/io.h>
+#include <linux/ptrace.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/errno.h>
+
+#include "hermes.h"
+
+#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */
+#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */
+#define CMD_COMPL_TIMEOUT (10000) /* in iterations of ~10us */
+#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */
+#define BAP_BUSY_TIMEOUT (500) /* In iterations of ~1us */
+#define BAP_ERROR_RETRY (10) /* How many times to retry a BAP seek when there is an error */
+
+#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
+#define MIN(a, b) ( (a) < (b) ? (a) : (b) )
+
+/*
+ * Debugging helpers
+ */
+
+#undef HERMES_DEBUG
+#ifdef HERMES_DEBUG
+
+#include <stdarg.h>
+
+#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \
+			printk(#stuff);} while (0)
+
+#define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(#stuff)
+
+#else /* ! HERMES_DEBUG */
+
+#define DEBUG(lvl, stuff...) do { } while (0)
+
+#endif /* ! HERMES_DEBUG */
+
+/*
+ * Prototypes
+ */
+
+static int hermes_issue_cmd(hermes_t *hw, uint16_t cmd, uint16_t param0);
+
+/*
+ * Internal inline functions
+ */
+
+/*
+ * Internal functions
+ */
+
+/* Issue a command to the chip. Waiting for it to complete is the caller's
+   problem.
+
+   Returns -EBUSY if the command register is busy, 0 on success.
+
+   Callable from any context.
+*/
+static int hermes_issue_cmd(hermes_t *hw, uint16_t cmd, uint16_t param0)
+{
+	uint16_t reg;
+/*  	unsigned long k = CMD_BUSY_TIMEOUT; */
+
+	/* First check that the command register is not busy */
+	reg = hermes_read_regn(hw, CMD);
+	if (reg & HERMES_CMD_BUSY) {
+		return -EBUSY;
+	}
+
+	hermes_write_regn(hw, PARAM2, 0);
+	hermes_write_regn(hw, PARAM1, 0);
+	hermes_write_regn(hw, PARAM0, param0);
+	hermes_write_regn(hw, CMD, cmd);
+	
+	return 0;
+}
+
+/*
+ * Function definitions
+ */
+
+void hermes_struct_init(hermes_t *hw, ushort io)
+{
+	hw->iobase = io;
+	hw->inten = 0x0;
+}
+
+int hermes_reset(hermes_t *hw)
+{
+	uint16_t status, reg;
+	int err = 0;
+	int k;
+
+	/* We don't want to be interrupted while resetting the chipset */
+	hw->inten = 0x0;
+	hermes_write_regn(hw, INTEN, 0);
+	hermes_write_regn(hw, EVACK, 0xffff);
+
+	/* Because we hope we can reset the card even if it gets into
+	   a stupid state, we actually wait to see if the command
+	   register will unbusy itself */
+	k = CMD_BUSY_TIMEOUT;
+	reg = hermes_read_regn(hw, CMD);
+	while (k && (reg & HERMES_CMD_BUSY)) {
+		if (reg == 0xffff) /* Special case - the card has probably been removed,
+				      so don't wait for the timeout */
+			return -ENODEV;
+
+		k--;
+		udelay(1);
+		reg = hermes_read_regn(hw, CMD);
+	}
+	
+	/* No need to explicitly handle the timeout - hermes_issue_cmd() will
+	   probably return -EBUSY */
+
+	/* We don't use hermes_docmd_wait here, because the reset wipes
+	   the magic constant in SWSUPPORT0 away, and it gets confused */
+	err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0);
+	if (err)
+		return err;
+
+	reg = hermes_read_regn(hw, EVSTAT);
+	k = CMD_INIT_TIMEOUT;
+	while ( (! (reg & HERMES_EV_CMD)) && k) {
+		k--;
+		udelay(10);
+		reg = hermes_read_regn(hw, EVSTAT);
+	}
+
+	DEBUG(0, "Reset completed in %d iterations\n", CMD_INIT_TIMEOUT - k);
+
+	hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);
+
+	if (! hermes_present(hw)) {
+		DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n",
+		       hw->iobase);
+		err = -ENODEV;
+		goto out;
+	}
+		
+	if (! (reg & HERMES_EV_CMD)) {
+		printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for card to reset (reg=0x%04x)!\n",
+		       hw->iobase, reg);
+		err = -ETIMEDOUT;
+		goto out;
+	}
+
+	status = hermes_read_regn(hw, STATUS);
+
+	hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
+
+	err = status & HERMES_STATUS_RESULT;
+
+ out:
+	return err;
+}
+
+/* Issue a command to the chip, and (busy!) wait for it to
+ * complete.
+ *
+ * Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware
+ *
+ * Callable from any context, but locking is your problem. */
+int hermes_docmd_wait(hermes_t *hw, uint16_t cmd, uint16_t parm0, hermes_response_t *resp)
+{
+	int err;
+	int k;
+	uint16_t reg;
+
+	err = hermes_issue_cmd(hw, cmd, parm0);
+	if (err) {
+		if (! hermes_present(hw)) {
+			printk(KERN_WARNING "hermes @ 0x%x: Card removed while issuing command.\n",
+			       hw->iobase);
+			err = -ENODEV;
+		} else 
+			printk(KERN_ERR "hermes @ 0x%x: CMD register busy in hermes_issue_command().\n",
+			       hw->iobase);
+		goto out;
+	}
+
+	reg = hermes_read_regn(hw, EVSTAT);
+	k = CMD_COMPL_TIMEOUT;
+	while ( (! (reg & HERMES_EV_CMD)) && k) {
+		k--;
+		udelay(10);
+		reg = hermes_read_regn(hw, EVSTAT);
+	}
+
+	if (! hermes_present(hw)) {
+		printk(KERN_WARNING "hermes @ 0x%x: Card removed while waiting for command completion.\n",
+		       hw->iobase);
+		err = -ENODEV;
+		goto out;
+	}
+		
+	if (! (reg & HERMES_EV_CMD)) {
+		printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for command completion.\n",
+		       hw->iobase);
+		err = -ETIMEDOUT;
+		goto out;
+	}
+
+	resp->status = hermes_read_regn(hw, STATUS);
+	resp->resp0 = hermes_read_regn(hw, RESP0);
+	resp->resp1 = hermes_read_regn(hw, RESP1);
+	resp->resp2 = hermes_read_regn(hw, RESP2);
+
+	hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
+
+	err = resp->status & HERMES_STATUS_RESULT;
+
+ out:
+	return err;
+}
+
+int hermes_allocate(hermes_t *hw, uint16_t size, uint16_t *fid)
+{
+	int err = 0;
+	hermes_response_t resp;
+	int k;
+	uint16_t reg;
+	
+	if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) )
+		return -EINVAL;
+
+	err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, &resp);
+	if (err)
+		return err;
+
+	reg = hermes_read_regn(hw, EVSTAT);
+	k = ALLOC_COMPL_TIMEOUT;
+	while ( (! (reg & HERMES_EV_ALLOC)) && k) {
+		k--;
+		udelay(10);
+		reg = hermes_read_regn(hw, EVSTAT);
+	}
+	
+	if (! hermes_present(hw)) {
+		printk(KERN_WARNING "hermes @ 0x%x: Card removed waiting for frame allocation.\n",
+		       hw->iobase);
+		return -ENODEV;
+	}
+		
+	if (! (reg & HERMES_EV_ALLOC)) {
+		printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for frame allocation\n",
+		       hw->iobase);
+		return -ETIMEDOUT;
+	}
+
+	*fid = hermes_read_regn(hw, ALLOCFID);
+	hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC);
+	
+	return 0;
+}
+
+/* Set up a BAP to read a particular chunk of data from card's internal buffer.
+ *
+ * Returns: < 0 on internal failure (errno), 0 on success, >0 on error
+ * from firmware
+ *
+ * Callable from any context */
+static int hermes_bap_seek(hermes_t *hw, int bap, uint16_t id, uint16_t offset)
+{
+	int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0;
+	int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;
+	int k;
+	int l = BAP_ERROR_RETRY;
+	uint16_t reg;
+
+	/* Paranoia.. */
+	if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) )
+		return -EINVAL;
+
+	k = BAP_BUSY_TIMEOUT;
+	reg = hermes_read_reg(hw, oreg);
+
+	if (reg & HERMES_OFFSET_BUSY)
+		return -EBUSY;
+
+	/* Now we actually set up the transfer */
+ retry:
+	hermes_write_reg(hw, sreg, id);
+	hermes_write_reg(hw, oreg, offset);
+
+	/* Wait for the BAP to be ready */
+	k = BAP_BUSY_TIMEOUT;
+	reg = hermes_read_reg(hw, oreg);
+	while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) {
+		k--;
+		udelay(1);
+		reg = hermes_read_reg(hw, oreg);
+	}
+
+	if (reg & HERMES_OFFSET_BUSY)
+		return -ETIMEDOUT;
+
+	/* For some reason, seeking the BAP seems to randomly fail somewhere
+	   (firmware bug?). We retry a few times before giving up. */
+	if (reg & HERMES_OFFSET_ERR) {
+		if (l--) {
+			udelay(1);
+			goto retry;
+		} else
+			return -EIO;
+	}
+
+	return 0;
+}
+
+/* Read a block of data from the chip's buffer, via the
+ * BAP. Synchronization/serialization is the caller's problem.  len
+ * must be even.
+ *
+ * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware
+ */
+int hermes_bap_pread(hermes_t *hw, int bap, void *buf, uint16_t len,
+		     uint16_t id, uint16_t offset)
+{
+	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
+	int err = 0;
+
+	if (len % 2)
+		return -EINVAL;
+
+	err = hermes_bap_seek(hw, bap, id, offset);
+	if (err)
+		goto out;
+
+	/* Actually do the transfer */
+	hermes_read_data(hw, dreg, buf, len/2);
+
+ out:
+	return err;
+}
+
+/* Write a block of data to the chip's buffer, via the
+ * BAP. Synchronization/serialization is the caller's problem. len
+ * must be even.
+ *
+ * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware
+ */
+int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, uint16_t len,
+		      uint16_t id, uint16_t offset)
+{
+	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
+	int err = 0;
+
+	if (len % 2)
+		return -EINVAL;
+
+	err = hermes_bap_seek(hw, bap, id, offset);
+	if (err)
+		goto out;
+	
+	/* Actually do the transfer */
+	hermes_write_data(hw, dreg, buf, len/2);
+
+ out:	
+	return err;
+}
+
+/* Read a Length-Type-Value record from the card.
+ *
+ * If length is NULL, we ignore the length read from the card, and
+ * read the entire buffer regardless. This is useful because some of
+ * the configuration records appear to have incorrect lengths in
+ * practice.
+ *
+ * Callable from user or bh context.  */
+int hermes_read_ltv(hermes_t *hw, int bap, uint16_t rid, int buflen,
+		    uint16_t *length, void *buf)
+{
+	int err = 0;
+	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
+	uint16_t rlength, rtype;
+	hermes_response_t resp;
+	int count;
+
+	if (buflen % 2)
+		return -EINVAL;
+
+	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, &resp);
+	if (err)
+		goto out;
+
+	err = hermes_bap_seek(hw, bap, rid, 0);
+	if (err)
+		goto out;
+
+	rlength = hermes_read_reg(hw, dreg);
+	rtype = hermes_read_reg(hw, dreg);
+
+	if (length)
+		*length = rlength;
+
+	if (rtype != rid)
+		printk(KERN_WARNING "hermes_read_ltv(): rid  (0x%04x) does "
+		       "not match type (0x%04x)\n", rid, rtype);
+	if (HERMES_RECLEN_TO_BYTES(rlength) > buflen)
+		printk(KERN_WARNING "hermes @ 0x%x: Truncating LTV record from %d to %d bytes. "
+		       "(rid=0x%04x, len=0x%04x)\n", hw->iobase,
+		       HERMES_RECLEN_TO_BYTES(rlength), buflen, rid, rlength);
+	
+	/* For now we always read the whole buffer, the
+	   lengths in the records seem to be wrong, frequently */
+	count = buflen / 2;
+
+#if 0
+	if (length)
+		count = (MIN(buflen, rlength) + 1) / 2;
+	else {
+		count = buflen / 2;
+		if (rlength != buflen)
+			printk(KERN_WARNING "hermes_read_ltv(): Incorrect \
+record length %d instead of %d on RID 0x%04x\n", rlength, buflen, rid);
+	}
+#endif
+	hermes_read_data(hw, dreg, buf, count);
+
+ out:
+	return err;
+}
+
+int hermes_write_ltv(hermes_t *hw, int bap, uint16_t rid, 
+		     uint16_t length, const void *value)
+{
+	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
+	int err = 0;
+	hermes_response_t resp;
+	int count;
+	
+	DEBUG(3, "write_ltv(): bap=%d rid=0x%04x length=%d (value=0x%04x)\n",
+	      bap, rid, length, * ((uint16_t *)value));
+
+	err = hermes_bap_seek(hw, bap, rid, 0);
+	if (err)
+		goto out;
+
+	hermes_write_reg(hw, dreg, length);
+	hermes_write_reg(hw, dreg, rid);
+
+	count = length - 1;
+
+	hermes_write_data(hw, dreg, value, count);
+
+	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, 
+				rid, &resp);
+
+ out:
+	return err;
+}
+
+EXPORT_SYMBOL(hermes_struct_init);
+EXPORT_SYMBOL(hermes_reset);
+EXPORT_SYMBOL(hermes_docmd_wait);
+EXPORT_SYMBOL(hermes_allocate);
+
+EXPORT_SYMBOL(hermes_bap_pread);
+EXPORT_SYMBOL(hermes_bap_pwrite);
+EXPORT_SYMBOL(hermes_read_ltv);
+EXPORT_SYMBOL(hermes_write_ltv);
+
+static int __init init_hermes(void)
+{
+	printk(KERN_INFO "%s\n", version);
+
+	return 0;
+}
+
+module_init(init_hermes);
diff -urN linux-2.4.3-pre6/drivers/net/pcmcia/hermes.h linux/drivers/net/pcmcia/hermes.h
--- linux-2.4.3-pre6/drivers/net/pcmcia/hermes.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/pcmcia/hermes.h	Mon Dec  3 22:33:04 2001
@@ -0,0 +1,391 @@
+/* hermes.h
+ *
+ * Driver core for the "Hermes" wireless MAC controller, as used in
+ * the Lucent Orinoco and Cabletron RoamAbout cards. It should also
+ * work on the hfa3841 and hfa3842 MAC controller chips used in the
+ * Prism I & II chipsets.
+ *
+ * This is not a complete driver, just low-level access routines for
+ * the MAC controller itself.
+ *
+ * Based on the prism2 driver from Absolute Value Systems' linux-wlan
+ * project, the Linux wvlan_cs driver, Lucent's HCF-Light
+ * (wvlan_hcf.c) library, and the NetBSD wireless driver.
+ *
+ * Copyright (C) 2000, David Gibson, Linuxcare Australia <hermes@gibson.dropbear.id.au>
+ *
+ * Portions taken from hfa384x.h, Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+ *
+ * This file distributed under the GPL, version 2.
+ */
+
+#ifndef _HERMES_H
+#define _HERMES_H
+
+/* Notes on locking:
+ *
+ * As a module of low level hardware access routines, there is no
+ * locking. Users of this module should ensure that they serialize
+ * access to the hermes_t structure, and to the hardware
+*/
+
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+
+/*
+ * Limits and constants
+ */
+#define		HERMES_ALLOC_LEN_MIN		((uint16_t)4)
+#define		HERMES_ALLOC_LEN_MAX		((uint16_t)2400)
+#define		HERMES_LTV_LEN_MAX		(34)
+#define		HERMES_BAP_DATALEN_MAX		((uint16_t)4096)
+#define		HERMES_BAP_OFFSET_MAX		((uint16_t)4096)
+#define		HERMES_PORTID_MAX		((uint16_t)7)
+#define		HERMES_NUMPORTS_MAX		((uint16_t)(HERMES_PORTID_MAX+1))
+#define		HERMES_PDR_LEN_MAX		((uint16_t)260)	/* in bytes, from EK */
+#define		HERMES_PDA_RECS_MAX		((uint16_t)200)	/* a guess */
+#define		HERMES_PDA_LEN_MAX		((uint16_t)1024)	/* in bytes, from EK */
+#define		HERMES_SCANRESULT_MAX		((uint16_t)35)
+#define		HERMES_CHINFORESULT_MAX		((uint16_t)8)
+#define		HERMES_FRAME_LEN_MAX		(2304)
+#define		HERMES_MAX_MULTICAST		(16)
+#define		HERMES_MAGIC			(0x7d1f)
+
+/*
+ * Hermes register offsets
+ */
+#define		HERMES_CMD			(0x00)
+#define		HERMES_PARAM0			(0x02)
+#define		HERMES_PARAM1			(0x04)
+#define		HERMES_PARAM2			(0x06)
+#define		HERMES_STATUS			(0x08)
+#define		HERMES_RESP0			(0x0A)
+#define		HERMES_RESP1			(0x0C)
+#define		HERMES_RESP2			(0x0E)
+#define		HERMES_INFOFID			(0x10)
+#define		HERMES_RXFID			(0x20)
+#define		HERMES_ALLOCFID			(0x22)
+#define		HERMES_TXCOMPLFID		(0x24)
+#define		HERMES_SELECT0			(0x18)
+#define		HERMES_OFFSET0			(0x1C)
+#define		HERMES_DATA0			(0x36)
+#define		HERMES_SELECT1			(0x1A)
+#define		HERMES_OFFSET1			(0x1E)
+#define		HERMES_DATA1			(0x38)
+#define		HERMES_EVSTAT			(0x30)
+#define		HERMES_INTEN			(0x32)
+#define		HERMES_EVACK			(0x34)
+#define		HERMES_CONTROL			(0x14)
+#define		HERMES_SWSUPPORT0		(0x28)
+#define		HERMES_SWSUPPORT1		(0x2A)
+#define		HERMES_SWSUPPORT2		(0x2C)
+#define		HERMES_AUXPAGE			(0x3A)
+#define		HERMES_AUXOFFSET		(0x3C)
+#define		HERMES_AUXDATA			(0x3E)
+
+/*
+ * CMD register bitmasks
+ */
+#define		HERMES_CMD_BUSY			((uint16_t)0x8000)
+#define		HERMES_CMD_AINFO		((uint16_t)0x7f00)
+#define		HERMES_CMD_MACPORT		((uint16_t)0x0700)
+#define		HERMES_CMD_RECL			((uint16_t)0x0100)
+#define		HERMES_CMD_WRITE		((uint16_t)0x0100)
+#define		HERMES_CMD_PROGMODE		((uint16_t)0x0300)
+#define		HERMES_CMD_CMDCODE		((uint16_t)0x003f)
+
+/*
+ * STATUS register bitmasks
+ */
+#define		HERMES_STATUS_RESULT		((uint16_t)0x7f00)
+#define		HERMES_STATUS_CMDCODE		((uint16_t)0x003f)
+
+/*
+ * OFFSET refister bitmasks
+ */
+#define		HERMES_OFFSET_BUSY		((uint16_t)0x8000)
+#define		HERMES_OFFSET_ERR		((uint16_t)0x4000)
+#define		HERMES_OFFSET_DATAOFF		((uint16_t)0x0ffe)
+
+/*
+ * Event register bitmasks (INTEN, EVSTAT, EVACK)
+ */
+#define		HERMES_EV_TICK			((uint16_t)0x8000)
+#define		HERMES_EV_WTERR			((uint16_t)0x4000)
+#define		HERMES_EV_INFDROP		((uint16_t)0x2000)
+#define		HERMES_EV_INFO			((uint16_t)0x0080)
+#define		HERMES_EV_DTIM			((uint16_t)0x0020)
+#define		HERMES_EV_CMD			((uint16_t)0x0010)
+#define		HERMES_EV_ALLOC			((uint16_t)0x0008)
+#define		HERMES_EV_TXEXC			((uint16_t)0x0004)
+#define		HERMES_EV_TX			((uint16_t)0x0002)
+#define		HERMES_EV_RX			((uint16_t)0x0001)
+
+/*
+ * Command codes
+ */
+/*--- Controller Commands --------------------------*/
+#define		HERMES_CMD_INIT			((uint16_t)0x00)
+#define		HERMES_CMD_ENABLE		((uint16_t)0x01)
+#define		HERMES_CMD_DISABLE		((uint16_t)0x02)
+#define		HERMES_CMD_DIAG			((uint16_t)0x03)
+
+/*--- Buffer Mgmt Commands --------------------------*/
+#define		HERMES_CMD_ALLOC		((uint16_t)0x0A)
+#define		HERMES_CMD_TX			((uint16_t)0x0B)
+#define		HERMES_CMD_CLRPRST		((uint16_t)0x12)
+
+/*--- Regulate Commands --------------------------*/
+#define		HERMES_CMD_NOTIFY		((uint16_t)0x10)
+#define		HERMES_CMD_INQ			((uint16_t)0x11)
+
+/*--- Configure Commands --------------------------*/
+#define		HERMES_CMD_ACCESS		((uint16_t)0x21)
+#define		HERMES_CMD_DOWNLD		((uint16_t)0x22)
+
+/*--- Debugging Commands -----------------------------*/
+#define 	HERMES_CMD_MONITOR		((uint16_t)(0x38))
+#define		HERMES_MONITOR_ENABLE		((uint16_t)(0x0b))
+#define		HERMES_MONITOR_DISABLE		((uint16_t)(0x0f))
+
+/*
+ * Configuration RIDs
+ */
+
+#define		HERMES_RID_CNF_PORTTYPE		((uint16_t)0xfc00)
+#define		HERMES_RID_CNF_MACADDR		((uint16_t)0xfc01)
+#define		HERMES_RID_CNF_DESIRED_SSID	((uint16_t)0xfc02)
+#define		HERMES_RID_CNF_CHANNEL		((uint16_t)0xfc03)
+#define		HERMES_RID_CNF_OWN_SSID		((uint16_t)0xfc04)
+#define		HERMES_RID_CNF_SYSTEM_SCALE	((uint16_t)0xfc06)
+#define		HERMES_RID_CNF_MAX_DATA_LEN	((uint16_t)0xfc07)
+#define		HERMES_RID_CNF_PM_ENABLE	((uint16_t)0xfc09)
+#define		HERMES_RID_CNF_PM_MCAST_RX	((uint16_t)0xfc0b)
+#define		HERMES_RID_CNF_PM_PERIOD	((uint16_t)0xfc0c)
+#define		HERMES_RID_CNF_NICKNAME		((uint16_t)0xfc0e)
+#define		HERMES_RID_CNF_WEP_ON		((uint16_t)0xfc20)
+#define		HERMES_RID_CNF_MWO_ROBUST	((uint16_t)0xfc25)
+#define		HERMES_RID_CNF_PRISM2_WEP_ON	((uint16_t)0xfc28)
+#define		HERMES_RID_CNF_MULTICAST_LIST	((uint16_t)0xfc80)
+#define		HERMES_RID_CNF_CREATEIBSS	((uint16_t)0xfc81)
+#define		HERMES_RID_CNF_FRAG_THRESH	((uint16_t)0xfc82)
+#define		HERMES_RID_CNF_RTS_THRESH	((uint16_t)0xfc83)
+#define		HERMES_RID_CNF_TX_RATE_CTRL	((uint16_t)0xfc84)
+#define		HERMES_RID_CNF_PROMISCUOUS	((uint16_t)0xfc85)
+#define		HERMES_RID_CNF_KEYS		((uint16_t)0xfcb0)
+#define		HERMES_RID_CNF_TX_KEY		((uint16_t)0xfcb1)
+#define		HERMES_RID_CNF_TICKTIME		((uint16_t)0xfce0)
+
+#define		HERMES_RID_CNF_PRISM2_TX_KEY	((uint16_t)0xfc23)
+#define		HERMES_RID_CNF_PRISM2_KEY0	((uint16_t)0xfc24)
+#define		HERMES_RID_CNF_PRISM2_KEY1	((uint16_t)0xfc25)
+#define		HERMES_RID_CNF_PRISM2_KEY2	((uint16_t)0xfc26)
+#define		HERMES_RID_CNF_PRISM2_KEY3	((uint16_t)0xfc27)
+
+/*
+ * Information RIDs
+ */
+#define		HERMES_RID_CHANNEL_LIST		((uint16_t)0xfd10)
+#define		HERMES_RID_STAIDENTITY		((uint16_t)0xfd20)
+#define		HERMES_RID_CURRENT_SSID		((uint16_t)0xfd41)
+#define		HERMES_RID_CURRENT_BSSID	((uint16_t)0xfd42)
+#define		HERMES_RID_COMMSQUALITY		((uint16_t)0xfd43)
+#define 	HERMES_RID_CURRENT_TX_RATE	((uint16_t)0xfd44)
+#define		HERMES_RID_WEP_AVAIL		((uint16_t)0xfd4f)
+#define		HERMES_RID_CURRENT_CHANNEL	((uint16_t)0xfdc1)
+#define		HERMES_RID_DATARATES		((uint16_t)0xfdc6)
+
+/*
+ * Frame structures and constants
+ */
+
+typedef struct hermes_frame_desc {
+	/* Hermes - i.e. little-endian byte-order */
+	uint16_t status; /* 0x0 */
+	uint16_t res1, res2; /* 0x2, 0x4 */
+	uint16_t q_info; /* 0x6 */
+	uint16_t res3, res4; /* 0x8, 0xA */
+	uint16_t tx_ctl; /* 0xC */
+} __attribute__ ((packed)) hermes_frame_desc_t;
+
+#define		HERMES_RXSTAT_ERR		((uint16_t)0x0003)
+#define		HERMES_RXSTAT_MACPORT		((uint16_t)0x0700)
+#define		HERMES_RXSTAT_MSGTYPE		((uint16_t)0xE000)
+
+#define		HERMES_RXSTAT_BADCRC		((uint16_t)0x0001)
+#define		HERMES_RXSTAT_UNDECRYPTABLE	((uint16_t)0x0002)
+
+/* RFC-1042 encoded frame */
+#define		HERMES_RXSTAT_1042		((uint16_t)0x2000)
+/* Bridge-tunnel encoded frame */
+#define		HERMES_RXSTAT_TUNNEL		((uint16_t)0x4000)
+/* Wavelan-II Management Protocol frame */
+#define		HERMES_RXSTAT_WMP		((uint16_t)0x6000)
+
+#ifdef __KERNEL__
+
+/* Basic control structure */
+typedef struct hermes {
+	uint iobase;
+
+	uint16_t inten; /* Which interrupts should be enabled? */
+} hermes_t;
+
+typedef struct hermes_response {
+	uint16_t status, resp0, resp1, resp2;
+} hermes_response_t;
+
+/* Firmware information structure */
+typedef struct hermes_identity {
+	uint16_t id, vendor, major, minor;
+} __attribute__ ((packed)) hermes_identity_t;
+
+/* "ID" structure - used for ESSID and station nickname */
+typedef struct hermes_id {
+	uint16_t len;
+	uint16_t val[16];
+} __attribute__ ((packed)) hermes_id_t;
+
+typedef struct hermes_commsqual {
+	uint16_t qual, signal, noise;
+} __attribute__ ((packed)) hermes_commsqual_t;
+
+typedef struct hermes_multicast {
+	uint8_t addr[HERMES_MAX_MULTICAST][ETH_ALEN];
+} __attribute__ ((packed)) hermes_multicast_t;
+
+/* Register access convenience macros */
+#define hermes_read_reg(hw, off) (inw((hw)->iobase + (off)))
+#define hermes_write_reg(hw, off, val) (outw_p((val), (hw)->iobase + (off)))
+
+#define hermes_read_regn(hw, name) (hermes_read_reg((hw), HERMES_##name))
+#define hermes_write_regn(hw, name, val) (hermes_write_reg((hw), HERMES_##name, (val)))
+
+/* Note that for the next two, the count is in 16-bit words, not bytes */
+#define hermes_read_data(hw, off, buf, count) (insw((hw)->iobase + (off), (buf), (count)))
+#define hermes_write_data(hw, off, buf, count) (outsw((hw)->iobase + (off), (buf), (count)))
+
+/* Function prototypes */
+void hermes_struct_init(hermes_t *hw, ushort io);
+int hermes_reset(hermes_t *hw);
+int hermes_docmd_wait(hermes_t *hw, uint16_t cmd, uint16_t parm0, hermes_response_t *resp);
+int hermes_allocate(hermes_t *hw, uint16_t size, uint16_t *fid);
+
+
+int hermes_bap_pread(hermes_t *hw, int bap, void *buf, uint16_t len,
+		       uint16_t id, uint16_t offset);
+int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, uint16_t len,
+			uint16_t id, uint16_t offset);
+int hermes_read_ltv(hermes_t *hw, int bap, uint16_t rid, int buflen,
+		    uint16_t *length, void *buf);
+int hermes_write_ltv(hermes_t *hw, int bap, uint16_t rid,
+		      uint16_t length, const void *value);
+
+/* Inline functions */
+
+static inline int hermes_present(hermes_t *hw)
+{
+	return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC;
+}
+
+static inline void hermes_enable_interrupt(hermes_t *hw, uint16_t events)
+{
+	hw->inten |= events;
+	hermes_write_regn(hw, INTEN, hw->inten);
+}
+
+static inline void hermes_set_irqmask(hermes_t *hw, uint16_t events)
+{
+	hw->inten = events;
+	hermes_write_regn(hw, INTEN, events);
+}
+
+static inline int hermes_enable_port(hermes_t *hw, int port)
+{
+	hermes_response_t resp;
+
+	return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
+				 0, &resp);
+}
+
+static inline int hermes_disable_port(hermes_t *hw, int port)
+{
+	hermes_response_t resp;
+
+	return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), 
+				 0, &resp);
+}
+
+#define HERMES_BYTES_TO_RECLEN(n) ( ((n) % 2) ? (((n)+1)/2)+1 : ((n)/2)+1 )
+#define HERMES_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 )
+
+#define HERMES_READ_RECORD(hw, bap, rid, buf) \
+	(hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf)))
+#define HERMES_WRITE_RECORD(hw, bap, rid, buf) \
+	(hermes_write_ltv((hw),(bap),(rid),HERMES_BYTES_TO_RECLEN(sizeof(*buf)),(buf)))
+
+static inline int hermes_read_wordrec(hermes_t *hw, int bap, uint16_t rid, uint16_t *word)
+{
+	uint16_t rec;
+	int err;
+
+	err = HERMES_READ_RECORD(hw, bap, rid, &rec);
+	*word = le16_to_cpu(rec);
+	return err;
+}
+
+static inline int hermes_write_wordrec(hermes_t *hw, int bap, uint16_t rid, uint16_t word)
+{
+	uint16_t rec = cpu_to_le16(word);
+	return HERMES_WRITE_RECORD(hw, bap, rid, &rec);
+}
+
+static inline int hermes_read_staidentity(hermes_t *hw, int bap, hermes_identity_t *buf)
+{
+	int err;
+
+	err = HERMES_READ_RECORD(hw, bap, HERMES_RID_STAIDENTITY, buf);
+	if (err)
+		return err;
+
+	le16_to_cpus(&buf->id);
+	le16_to_cpus(&buf->vendor);
+	le16_to_cpus(&buf->major);
+	le16_to_cpus(&buf->minor);
+	
+	return 0;
+}
+
+static inline int hermes_read_commsqual(hermes_t *hw, int bap, hermes_commsqual_t *buf)
+{
+	int err;
+
+	err = HERMES_READ_RECORD(hw, bap, HERMES_RID_COMMSQUALITY, buf);
+	if (err)
+		return err;
+
+	le16_to_cpus(&buf->qual);
+	le16_to_cpus(&buf->signal);
+	le16_to_cpus(&buf->noise);
+	
+	return 0;
+}
+
+#else /* ! __KERNEL__ */
+
+/* These are provided for the benefit of userspace drivers and testing programs
+   which use ioperm() or iopl() */
+
+#define hermes_read_reg(base, off) (inw((base) + (off)))
+#define hermes_write_reg(base, off, val) (outw((val), (base) + (off)))
+
+#define hermes_read_regn(base, name) (hermes_read_reg((base), HERMES_##name))
+#define hermes_write_regn(base, name, val) (hermes_write_reg((base), HERMES_##name, (val)))
+
+/* Note that for the next two, the count is in 16-bit words, not bytes */
+#define hermes_read_data(base, off, buf, count) (insw((base) + (off), (buf), (count)))
+#define hermes_write_data(base, off, buf, count) (outsw((base) + (off), (buf), (count)))
+
+#endif /* ! __KERNEL__ */
+
+#endif  /* _HERMES_H */
diff -urN linux-2.4.3-pre6/drivers/net/pcmcia/orinoco_cs.c linux/drivers/net/pcmcia/orinoco_cs.c
--- linux-2.4.3-pre6/drivers/net/pcmcia/orinoco_cs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/pcmcia/orinoco_cs.c	Mon Dec  3 22:33:04 2001
@@ -0,0 +1,3562 @@
+/* dldwd_cs.c 0.01
+ *
+ * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
+ * as the Lucent/Orinoco cards and the Cabletron RoamAbout. It should
+ * also be usable on Prism II based cards such as the Farallon Skyline
+ * and the 3Com AirConnect.
+ *
+ * Copyright (C) 2000 David Gibson, Linuxcare Australia <hermes@gibson.dropbear.id.au>
+ *
+ * Based on dummy_cs.c 1.27 2000/06/12 21:27:25
+ *
+ * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus <andy@fasta.fh-dortmund.de>
+ *      http://www.fasta.fh-dortmund.de/users/andy/wvlan/
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License
+ * at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David
+ * A. Hinds are Copyright (C) 1999 David A. Hinds.  All Rights
+ * Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the above.
+ * If you wish to allow the use of your version of this file only
+ * under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL.  If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the MPL or the GPL.
+ */
+
+/* Notes on locking:
+ *
+ * The basic principle of operation is that everything except the
+ * interrupt handler is serialized through a single spinlock in the
+ * dldwd_priv_t structure, using dldwd_lock() and
+ * dldwd_unlock() (which in turn use spin_lock_bh() and spin_unlock_bh()).
+ *
+ * The kernel's IRQ handling stuff ensures that the interrupt handler
+ * does not re-enter itself. The interrupt handler is written such
+ * that everything it does is safe without a lock: chiefly this means
+ * that the Rx path uses one of the Hermes chipset's BAPs while
+ * everything else uses the other.
+ *
+ * For the moment access to the device statistics from the interrupt
+ * handler is unsafe - we just put up with any resulting errors in the
+ * statisics. FIXME: This should probably be changed to store the
+ * stats in atomic types.
+ *
+ * EXCEPT that we don't want the irq handler running when we actually
+ * reset or shut down the card, because strange things might happen
+ * (probably the worst would be one packet of garbage, but you can't
+ * be too careful). For this we use __dldwd_stop_irqs() which will set
+ * a flag to disable the interrupt handler, and wait for any
+ * outstanding instances of the handler to complete. THIS WILL LOSE
+ * INTERRUPTS! so it shouldn't be used except for resets, when we
+ * don't care about that.*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/bus_ops.h>
+
+#include "hermes.h"
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+static char *version = "dldwd_cs.c 0.01 (David Gibson <hermes@gibson.dropbear.id.au>)";
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#define DEBUGMORE(n, args...) do { if (pc_debug>(n)) printk(args); } while (0)
+#else
+#define DEBUG(n, args...) do { } while (0)
+#define DEBUGMORE(n, args...) do { } while (0)
+#endif
+
+#define TRACE_ENTER(devname) DEBUG(2, "%s: -> " __FUNCTION__ "()\n", devname);
+#define TRACE_EXIT(devname)  DEBUG(2, "%s: <- " __FUNCTION__ "()\n", devname);
+
+#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
+#define MIN(a, b) ( (a) < (b) ? (a) : (b) )
+
+#define RUP_EVEN(a) ( (a) % 2 ? (a) + 1 : (a) )
+
+
+#if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10)
+#error "dldwd_cs requires Wireless extensions v10 or later."
+#endif /* (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) */
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* The old way: bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static uint irq_mask = 0xdeb8;
+/* Newer, simpler way of listing specific interrupts */
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+#define DLDWD_MIN_MTU		256
+#define DLDWD_MAX_MTU		(HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)
+
+#define LTV_BUF_SIZE		128
+#define USER_BAP		0
+#define IRQ_BAP			1
+#define DLDWD_MACPORT		0
+#define IRQ_LOOP_MAX		10
+#define TX_NICBUF_SIZE		2048
+#define MAX_KEYS		4
+#define MAX_KEY_SIZE		14
+#define LARGE_KEY_SIZE		13
+#define SMALL_KEY_SIZE		5
+#define MAX_FRAME_SIZE		2304
+
+const long channel_frequency[] = {
+	2412, 2417, 2422, 2427, 2432, 2437, 2442,
+	2447, 2452, 2457, 2462, 2467, 2472, 2484
+};
+
+#define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
+
+/* This tables gives the actual meanings of the bitrate IDs returned by the firmware.
+   It gives the rate in halfMb/s, negative indicates auto mode */
+const int rate_list[] = { 0, 2, 4, -22, 11, 22, -4, -11, 0, 0, 0, 0};
+
+#define NUM_RATES (sizeof(rate_list) / sizeof(rate_list[0]))
+typedef struct dldwd_key {
+	uint16_t len;
+	char data[MAX_KEY_SIZE];
+} __attribute__ ((packed)) dldwd_key_t;
+
+typedef dldwd_key_t dldwd_keys_t[MAX_KEYS];
+
+typedef struct dldwd_priv {
+	dev_link_t link;
+	dev_node_t node;
+	int instance;
+
+	spinlock_t lock;
+	long state;
+#define DLDWD_STATE_INIRQ 0
+#define DLDWD_STATE_DOIRQ 1
+
+	/* Net device stuff */
+	struct net_device ndev;
+	struct net_device_stats stats;
+	struct iw_statistics wstats;
+
+
+	/* Hardware control variables */
+	hermes_t hw;
+	uint16_t txfid;
+
+	/* Capabilities of the hardware/firmware */
+	hermes_identity_t firmware_info;
+	int has_ibss, has_port3, prefer_port3;
+	int has_wep, has_big_wep, wep_type;
+#define WEP_TYPE_LUCENT 1
+#define WEP_TYPE_PRISM 2
+	int has_mwo;
+	int has_pm;
+	int broken_reset;
+	uint16_t channel_mask;
+
+	/* Current configuration */
+	uint32_t iw_mode;
+	int port_type, allow_ibss;
+	uint16_t wep_on, tx_key;
+	dldwd_keys_t keys;
+ 	char nick[IW_ESSID_MAX_SIZE+1];
+	char desired_essid[IW_ESSID_MAX_SIZE+1];
+	uint16_t frag_thresh, mwo_robust;
+	uint16_t channel;
+	uint16_t ap_density, rts_thresh;
+	uint16_t tx_rate_ctrl;
+	uint16_t pm_on, pm_mcast, pm_period;
+
+	int promiscuous, allmulti, mc_count;
+
+	/* /proc based debugging stuff */
+	struct proc_dir_entry *dir_dev;
+	struct proc_dir_entry *dir_regs;
+	struct proc_dir_entry *dir_recs;
+} dldwd_priv_t;
+
+struct p80211_hdr {
+	uint16_t frame_ctl;
+	uint16_t duration_id;
+	uint8_t addr1[ETH_ALEN];
+	uint8_t addr2[ETH_ALEN];
+	uint8_t addr3[ETH_ALEN];
+	uint16_t seq_ctl;
+	uint8_t addr4[ETH_ALEN];
+	uint16_t data_len;
+} __attribute__ ((packed));
+
+/* Frame control field constants */
+#define DLDWD_FCTL_VERS			0x0002
+#define DLDWD_FCTL_FTYPE		0x000c
+#define DLDWD_FCTL_STYPE		0x00f0
+#define DLDWD_FCTL_TODS			0x0100
+#define DLDWD_FCTL_FROMDS		0x0200
+#define DLDWD_FCTL_MOREFRAGS		0x0400
+#define DLDWD_FCTL_RETRY		0x0800
+#define DLDWD_FCTL_PM			0x1000
+#define DLDWD_FCTL_MOREDATA		0x2000
+#define DLDWD_FCTL_WEP			0x4000
+#define DLDWD_FCTL_ORDER		0x8000
+
+#define DLDWD_FTYPE_MGMT		0x0000
+#define DLDWD_FTYPE_CTL			0x0004
+#define DLDWD_FTYPE_DATA		0x0008
+
+struct p8022_hdr {
+	uint8_t dsap;
+	uint8_t ssap;
+	uint8_t ctrl;
+	uint8_t oui[3];
+} __attribute__ ((packed));
+
+struct dldwd_frame_hdr {
+	hermes_frame_desc_t desc;
+	struct p80211_hdr p80211;
+	struct ethhdr p8023;
+	struct p8022_hdr p8022;
+	uint16_t ethertype;
+} __attribute__ ((packed));
+
+#define P8023_OFFSET		(sizeof(hermes_frame_desc_t) + \
+				sizeof(struct p80211_hdr))
+#define ENCAPS_OVERHEAD		(sizeof(struct p8022_hdr) + 2)
+
+/*
+ * Function prototypes
+ */
+
+/* PCMCIA gumpf */
+
+static void dldwd_config(dev_link_t * link);
+static void dldwd_release(u_long arg);
+static int dldwd_event(event_t event, int priority,
+		       event_callback_args_t * args);
+
+static dev_link_t *dldwd_attach(void);
+static void dldwd_detach(dev_link_t *);
+
+/* Hardware control routines */
+
+static int __dldwd_hw_reset(dldwd_priv_t *priv);
+static void dldwd_shutdown(dldwd_priv_t *dev);
+static int dldwd_reset(dldwd_priv_t *dev);
+static int __dldwd_hw_setup_wep(dldwd_priv_t *priv);
+static int dldwd_hw_get_bssid(dldwd_priv_t *priv, char buf[ETH_ALEN]);
+static int dldwd_hw_get_essid(dldwd_priv_t *priv, int *active, char buf[IW_ESSID_MAX_SIZE+1]);
+static long dldwd_hw_get_freq(dldwd_priv_t *priv);
+static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates,
+				    int32_t *rates, int max);
+
+/* Interrupt handling routines */
+void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs);
+static void __dldwd_ev_tick(dldwd_priv_t *priv, hermes_t *hw);
+static void __dldwd_ev_wterr(dldwd_priv_t *priv, hermes_t *hw);
+static void __dldwd_ev_infdrop(dldwd_priv_t *priv, hermes_t *hw);
+static void __dldwd_ev_info(dldwd_priv_t *priv, hermes_t *hw);
+static void __dldwd_ev_rx(dldwd_priv_t *priv, hermes_t *hw);
+static void __dldwd_ev_txexc(dldwd_priv_t *priv, hermes_t *hw);
+static void __dldwd_ev_tx(dldwd_priv_t *priv, hermes_t *hw);
+static void __dldwd_ev_alloc(dldwd_priv_t *priv, hermes_t *hw);
+
+/* struct net_device methods */
+static int dldwd_init(struct net_device *dev);
+static int dldwd_open(struct net_device *dev);
+static int dldwd_stop(struct net_device *dev);
+
+static int dldwd_xmit(struct sk_buff *skb, struct net_device *dev);
+static void dldwd_tx_timeout(struct net_device *dev);
+
+static struct net_device_stats *dldwd_get_stats(struct net_device *dev);
+static struct iw_statistics *dldwd_get_wireless_stats(struct net_device *dev);
+
+static int dldwd_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq);
+static int dldwd_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq);
+static int dldwd_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq);
+static int dldwd_ioctl_setessid(struct net_device *dev, struct iw_point *erq);
+static int dldwd_ioctl_getessid(struct net_device *dev, struct iw_point *erq);
+static int dldwd_ioctl_setnick(struct net_device *dev, struct iw_point *nrq);
+static int dldwd_ioctl_getnick(struct net_device *dev, struct iw_point *nrq);
+static int dldwd_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq);
+static int dldwd_ioctl_getsens(struct net_device *dev, struct iw_param *srq);
+static int dldwd_ioctl_setsens(struct net_device *dev, struct iw_param *srq);
+static int dldwd_ioctl_setrts(struct net_device *dev, struct iw_param *rrq);
+static int dldwd_ioctl_setfrag(struct net_device *dev, struct iw_param *frq);
+static int dldwd_ioctl_getfrag(struct net_device *dev, struct iw_param *frq);
+static int dldwd_ioctl_setrate(struct net_device *dev, struct iw_param *frq);
+static int dldwd_ioctl_getrate(struct net_device *dev, struct iw_param *frq);
+static int dldwd_ioctl_setpower(struct net_device *dev, struct iw_param *prq);
+static int dldwd_ioctl_getpower(struct net_device *dev, struct iw_param *prq);
+static int dldwd_ioctl_setport3(struct net_device *dev, struct iwreq *wrq);
+static int dldwd_ioctl_getport3(struct net_device *dev, struct iwreq *wrq);
+static int dldwd_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+
+static int dldwd_change_mtu(struct net_device *dev, int new_mtu);
+static void __dldwd_set_multicast_list(struct net_device *dev);
+
+/* /proc debugging stuff */
+static int dldwd_proc_init(void);
+static void dldwd_proc_cleanup(void);
+static int dldwd_proc_dev_init(dldwd_priv_t *dev);
+static void dldwd_proc_dev_cleanup(dldwd_priv_t *dev);
+
+/*
+ * Inline functions
+ */
+static inline void
+dldwd_lock(dldwd_priv_t *priv)
+{
+	spin_lock_bh(&priv->lock);
+}
+
+static inline void
+dldwd_unlock(dldwd_priv_t *priv)
+{
+	spin_unlock_bh(&priv->lock);
+}
+
+static inline int
+dldwd_irqs_allowed(dldwd_priv_t *priv)
+{
+	return test_bit(DLDWD_STATE_DOIRQ, &priv->state);
+}
+
+static inline void
+__dldwd_stop_irqs(dldwd_priv_t *priv)
+{
+	hermes_t *hw = &priv->hw;
+
+	hermes_set_irqmask(hw, 0);
+	clear_bit(DLDWD_STATE_DOIRQ, &priv->state);
+	while (test_bit(DLDWD_STATE_INIRQ, &priv->state))
+		;
+}
+
+static inline void
+__dldwd_start_irqs(dldwd_priv_t *priv, uint16_t irqmask)
+{
+	hermes_t *hw = &priv->hw;
+
+	TRACE_ENTER(priv->ndev.name);
+
+	__cli();
+	set_bit(DLDWD_STATE_DOIRQ, &priv->state);
+	hermes_set_irqmask(hw, irqmask);
+	__sti();
+
+	TRACE_EXIT(priv->ndev.name);
+}
+
+static inline void
+set_port_type(dldwd_priv_t *priv)
+{
+	switch (priv->iw_mode) {
+	case IW_MODE_INFRA:
+		priv->port_type = 1;
+		priv->allow_ibss = 0;
+		break;
+	case IW_MODE_ADHOC:
+		if (priv->prefer_port3) {
+			priv->port_type = 3;
+			priv->allow_ibss = 0;
+		} else {
+			priv->port_type = 1;
+			priv->allow_ibss = 1;
+		}
+		break;
+	default:
+		printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
+		       priv->ndev.name);
+	}
+}
+
+static inline void
+dldwd_set_multicast_list(struct net_device *dev)
+{
+	dldwd_priv_t *priv = dev->priv;
+
+	dldwd_lock(priv);
+	__dldwd_set_multicast_list(dev);
+	dldwd_unlock(priv);
+}
+
+/*
+ * Hardware control routines
+ */
+
+static int
+__dldwd_hw_reset(dldwd_priv_t *priv)
+{
+	hermes_t *hw = &priv->hw;
+	int err;
+
+	if (! priv->broken_reset)
+		return hermes_reset(hw);
+	else {
+		hw->inten = 0;
+		hermes_write_regn(hw, INTEN, 0);
+		err = hermes_disable_port(hw, 0);
+		hermes_write_regn(hw, EVACK, 0xffff);
+		return err;
+	}
+}
+
+static void
+dldwd_shutdown(dldwd_priv_t *priv)
+{
+/* 	hermes_t *hw = &priv->hw; */
+	int err = 0;
+
+	TRACE_ENTER(priv->ndev.name);
+
+	dldwd_lock(priv);
+	__dldwd_stop_irqs(priv);
+
+	err = __dldwd_hw_reset(priv);
+	if (err && err != -ENODEV) /* If the card is gone, we don't care about shutting it down */
+		printk(KERN_ERR "%s: Error %d shutting down Hermes chipset\n", priv->ndev.name, err);
+
+	dldwd_unlock(priv);
+
+	TRACE_EXIT(priv->ndev.name);
+}
+
+static int
+dldwd_reset(dldwd_priv_t *priv)
+{
+	struct net_device *dev = &priv->ndev;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	hermes_id_t idbuf;
+
+	TRACE_ENTER(priv->ndev.name);
+
+	dldwd_lock(priv);
+
+	__dldwd_stop_irqs(priv);
+
+	err = __dldwd_hw_reset(priv);
+	if (err)
+		goto out;
+
+	err = hermes_allocate(hw, TX_NICBUF_SIZE, &priv->txfid);
+	if (err)
+		goto out;
+
+	/* Now set up all the parameters on the card */
+	
+	/* Set up the link mode */
+	
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PORTTYPE, priv->port_type);
+	if (err)
+		goto out;
+	if (priv->has_ibss) {
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_CREATEIBSS,
+					   priv->allow_ibss);
+		if (err)
+			goto out;
+	}
+
+	/* Set up encryption */
+	err = __dldwd_hw_setup_wep(priv);
+	if (err)
+		goto out;
+
+	/* Set the desired ESSID */
+	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
+	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
+	err = hermes_write_ltv(hw, USER_BAP, (priv->port_type == 3) ?
+			       HERMES_RID_CNF_OWN_SSID : HERMES_RID_CNF_DESIRED_SSID,
+			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
+			       &idbuf);
+	if (err)
+		goto out;
+
+	/* Set the station name */
+	idbuf.len = cpu_to_le16(strlen(priv->nick));
+	memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
+	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNF_NICKNAME,
+			       HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
+			       &idbuf);
+	if (err)
+		goto out;
+
+	/* Set the channel/frequency */
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_CHANNEL, priv->channel);
+	if (err)
+		goto out;
+
+	/* Set AP density */
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, priv->ap_density);
+	if (err)
+		goto out;
+
+	/* Set RTS threshold */
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_RTS_THRESH, priv->rts_thresh);
+	if (err)
+		goto out;
+
+	/* Set fragmentation threshold or MWO robustness */
+	if (priv->has_mwo)
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNF_MWO_ROBUST, priv->mwo_robust);
+	else
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNF_FRAG_THRESH, priv->frag_thresh);
+	if (err)
+		goto out;
+
+	/* Set bitrate */
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_RATE_CTRL,
+				   priv->tx_rate_ctrl);
+	if (err)
+		goto out;
+
+	/* Set power management */
+	if (priv->has_pm) {
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_ENABLE,
+					   priv->pm_on);
+		if (err)
+			goto out;
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_MCAST_RX,
+					   priv->pm_mcast);
+		if (err)
+			goto out;
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD,
+					   priv->pm_period);
+		if (err)
+			goto out;
+	}
+
+	/* Set promiscuity / multicast*/
+	priv->promiscuous = 0;
+	priv->allmulti = 0;
+	priv->mc_count = 0;
+	__dldwd_set_multicast_list(dev);
+	
+	err = hermes_enable_port(hw, DLDWD_MACPORT);
+	if (err)
+		goto out;
+	
+	__dldwd_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC |
+			   HERMES_EV_TX | HERMES_EV_TXEXC |
+			   HERMES_EV_WTERR | HERMES_EV_INFO |
+			   HERMES_EV_INFDROP);
+
+ out:
+	dldwd_unlock(priv);
+
+	TRACE_EXIT(priv->ndev.name);
+
+	return err;
+}
+
+static int __dldwd_hw_setup_wep(dldwd_priv_t *priv)
+{
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	
+	switch (priv->wep_type) {
+	case 1: /* Lucent style WEP */
+		if (priv->wep_on) {
+			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_KEY, priv->tx_key);
+			if (err)
+				return err;
+			
+			err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_KEYS, &priv->keys);
+			if (err)
+				return err;
+		}
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_WEP_ON, priv->wep_on);
+		if (err)
+			return err;
+		break;
+
+	case 2: /* Prism II style WEP */
+		if (priv->wep_on) {
+			char keybuf[SMALL_KEY_SIZE+1];
+			
+			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PRISM2_TX_KEY,
+						   priv->tx_key);
+			if (err)
+				return err;
+			
+			keybuf[SMALL_KEY_SIZE] = '\0';
+
+			memcpy(keybuf, priv->keys[0].data, SMALL_KEY_SIZE);
+			err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_PRISM2_KEY0, &keybuf);
+			if (err)
+				return err;
+			memcpy(keybuf, priv->keys[1].data, SMALL_KEY_SIZE);
+			err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_PRISM2_KEY1, &keybuf);
+			if (err)
+				return err;
+			memcpy(keybuf, priv->keys[2].data, SMALL_KEY_SIZE);
+			err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_PRISM2_KEY2, &keybuf);
+			if (err)
+				return err;
+			memcpy(keybuf, priv->keys[3].data, SMALL_KEY_SIZE);
+			err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_PRISM2_KEY3, &keybuf);
+			if (err)
+				return err;
+		}
+		
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PRISM2_WEP_ON, priv->wep_on);
+		if (err)
+			return err;	
+		break;
+
+	default:
+		if (priv->wep_on) {
+			printk(KERN_ERR "%s: WEP enabled, although not supported!\n",
+			       priv->ndev.name);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+	return 0;
+}
+
+static int dldwd_hw_get_bssid(dldwd_priv_t *priv, char buf[ETH_ALEN])
+{
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+
+	dldwd_lock(priv);
+
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_BSSID,
+			      ETH_ALEN, NULL, buf);
+
+	dldwd_unlock(priv);
+
+	return err;
+}
+
+static int dldwd_hw_get_essid(dldwd_priv_t *priv, int *active,
+			      char buf[IW_ESSID_MAX_SIZE+1])
+{
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	hermes_id_t essidbuf;
+	char *p = (char *)(&essidbuf.val);
+	int len;
+
+	TRACE_ENTER(priv->ndev.name);
+
+	dldwd_lock(priv);
+
+	if (strlen(priv->desired_essid) > 0) {
+		/* We read the desired SSID from the hardware rather
+		   than from priv->desired_essid, just in case the
+		   firmware is allowed to change it on us. I'm not
+		   sure about this */
+		uint16_t rid;
+
+		*active = 1;
+
+		rid = (priv->port_type == 3) ? HERMES_RID_CNF_OWN_SSID :
+			HERMES_RID_CNF_DESIRED_SSID;
+		
+		err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
+				      NULL, &essidbuf);
+		if (err)
+			goto fail_unlock;
+	} else {
+		*active = 0;
+
+		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_SSID,
+				      sizeof(essidbuf), NULL, &essidbuf);
+		if (err)
+			goto fail_unlock;
+	}
+
+	len = le16_to_cpu(essidbuf.len);
+
+	memset(buf, 0, sizeof(buf));
+	memcpy(buf, p, len);
+	buf[len] = '\0';
+
+ fail_unlock:
+	dldwd_unlock(priv);
+
+	TRACE_EXIT(priv->ndev.name);
+
+	return err;       
+}
+
+static long dldwd_hw_get_freq(dldwd_priv_t *priv)
+{
+	
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	uint16_t channel;
+	long freq = 0;
+
+	dldwd_lock(priv);
+	
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_CHANNEL, &channel);
+	if (err)
+		goto out;
+
+	if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
+		struct net_device *dev = &priv->ndev;
+
+		printk(KERN_WARNING "%s: Channel out of range (%d)!\n", dev->name, channel);
+		err = -EBUSY;
+		goto out;
+
+	}
+	freq = channel_frequency[channel-1] * 100000;
+
+ out:
+	dldwd_unlock(priv);
+
+	if (err > 0)
+		err = -EBUSY;
+	return err ? err : freq;
+}
+
+static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates,
+				    int32_t *rates, int max)
+{
+	hermes_t *hw = &priv->hw;
+	hermes_id_t list;
+	unsigned char *p = (unsigned char *)&list.val;
+	int err = 0;
+	int num;
+	int i;
+
+	dldwd_lock(priv);
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_DATARATES, sizeof(list),
+			      NULL, &list);
+	dldwd_unlock(priv);
+
+	if (err)
+		return err;
+	
+	num = le16_to_cpu(list.len);
+	*numrates = num;
+	num = MIN(num, max);
+
+	for (i = 0; i < num; i++) {
+		rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
+	}
+
+	return 0;
+}
+
+#ifndef PCMCIA_DEBUG
+static inline void show_rx_frame(struct dldwd_frame_hdr *frame) {}
+#else
+static void show_rx_frame(struct dldwd_frame_hdr *frame)
+{
+	printk(KERN_DEBUG "RX descriptor:\n");
+	printk(KERN_DEBUG "  status      = 0x%04x\n", frame->desc.status);
+	printk(KERN_DEBUG "  res1        = 0x%04x\n", frame->desc.res1);
+	printk(KERN_DEBUG "  res2        = 0x%04x\n", frame->desc.res2);
+	printk(KERN_DEBUG "  q_info      = 0x%04x\n", frame->desc.q_info);
+	printk(KERN_DEBUG "  res3        = 0x%04x\n", frame->desc.res3);
+	printk(KERN_DEBUG "  res4        = 0x%04x\n", frame->desc.res4);
+	printk(KERN_DEBUG "  tx_ctl      = 0x%04x\n", frame->desc.tx_ctl);
+
+	printk(KERN_DEBUG "IEEE 802.11 header:\n");
+	printk(KERN_DEBUG "  frame_ctl   = 0x%04x\n",
+	       frame->p80211.frame_ctl);
+	printk(KERN_DEBUG "  duration_id = 0x%04x\n",
+	       frame->p80211.duration_id);
+	printk(KERN_DEBUG "  addr1       = %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       frame->p80211.addr1[0], frame->p80211.addr1[1],
+	       frame->p80211.addr1[2], frame->p80211.addr1[3],
+	       frame->p80211.addr1[4], frame->p80211.addr1[5]);
+	printk(KERN_DEBUG "  addr2       = %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       frame->p80211.addr2[0], frame->p80211.addr2[1],
+	       frame->p80211.addr2[2], frame->p80211.addr2[3],
+	       frame->p80211.addr2[4], frame->p80211.addr2[5]);
+	printk(KERN_DEBUG "  addr3       = %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       frame->p80211.addr3[0], frame->p80211.addr3[1],
+	       frame->p80211.addr3[2], frame->p80211.addr3[3],
+	       frame->p80211.addr3[4], frame->p80211.addr3[5]);
+	printk(KERN_DEBUG "  seq_ctl     = 0x%04x\n",
+	       frame->p80211.seq_ctl);
+	printk(KERN_DEBUG "  addr4       = %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       frame->p80211.addr4[0], frame->p80211.addr4[1],
+	       frame->p80211.addr4[2], frame->p80211.addr4[3],
+	       frame->p80211.addr4[4], frame->p80211.addr4[5]);
+	printk(KERN_DEBUG "  data_len    = 0x%04x\n",
+	       frame->p80211.data_len);
+
+	printk(KERN_DEBUG "IEEE 802.3 header:\n");
+	printk(KERN_DEBUG "  dest        = %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       frame->p8023.h_dest[0], frame->p8023.h_dest[1],
+	       frame->p8023.h_dest[2], frame->p8023.h_dest[3],
+	       frame->p8023.h_dest[4], frame->p8023.h_dest[5]);
+	printk(KERN_DEBUG "  src         = %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       frame->p8023.h_source[0], frame->p8023.h_source[1],
+	       frame->p8023.h_source[2], frame->p8023.h_source[3],
+	       frame->p8023.h_source[4], frame->p8023.h_source[5]);
+	printk(KERN_DEBUG "  len         = 0x%04x\n", frame->p8023.h_proto);
+
+	printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n");
+	printk(KERN_DEBUG "  DSAP        = 0x%02x\n", frame->p8022.dsap);
+	printk(KERN_DEBUG "  SSAP        = 0x%02x\n", frame->p8022.ssap);
+	printk(KERN_DEBUG "  ctrl        = 0x%02x\n", frame->p8022.ctrl);
+	printk(KERN_DEBUG "  OUI         = %02x:%02x:%02x\n",
+	       frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]);
+	printk(KERN_DEBUG "  ethertype  = 0x%04x\n", frame->ethertype);
+}
+#endif
+
+/*
+ * Interrupt handler
+ */
+void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs)
+{
+	dldwd_priv_t *priv = (dldwd_priv_t *) dev_id;
+	hermes_t *hw = &priv->hw;
+	struct net_device *dev = &priv->ndev;
+	int count = IRQ_LOOP_MAX;
+	uint16_t evstat, events;
+	static int old_time = 0, timecount = 0; /* Eugh, revolting hack for now */
+
+	if (test_and_set_bit(DLDWD_STATE_INIRQ, &priv->state))
+		BUG();
+
+	if (! dldwd_irqs_allowed(priv)) {
+		clear_bit(DLDWD_STATE_INIRQ, &priv->state);
+		return;
+	}
+
+	DEBUG(3, "%s: dldwd_interrupt()  irq %d\n", priv->ndev.name, irq);
+
+	while (1) {
+		if (jiffies != old_time)
+			timecount = 0;
+		if ( (++timecount > 50) || (! count--) ) {
+			printk(KERN_CRIT "%s: IRQ handler is looping too \
+much! Shutting down.\n",
+			       dev->name);
+			/* Perform an emergency shutdown */
+			clear_bit(DLDWD_STATE_DOIRQ, &priv->state);
+			hermes_set_irqmask(hw, 0);
+			break;
+		}
+
+		evstat = hermes_read_regn(hw, EVSTAT);
+		DEBUG(3, "__dldwd_interrupt(): count=%d EVSTAT=0x%04x inten=0x%04x\n",
+		      count, evstat, hw->inten);
+
+		events = evstat & hw->inten;
+
+		if (! events) {
+			if (netif_queue_stopped(dev)) {
+				/* There seems to be a firmware bug which
+				   sometimes causes the card to give an
+				   interrupt with no event set, when there
+				   sould be a Tx completed event. */
+				DEBUG(3, "%s: Interrupt with no event (ALLOCFID=0x%04x)\n",
+				      dev->name, (int)hermes_read_regn(hw, ALLOCFID));
+				events = HERMES_EV_TX | HERMES_EV_ALLOC;
+			} else /* Nothing's happening, we're done */
+				break;
+		}
+
+		/* Check the card hasn't been removed */
+		if (! hermes_present(hw)) {
+			DEBUG(0, "dldwd_interrupt(): card removed\n");
+			break;
+		}
+
+		if (events & HERMES_EV_TICK)
+			__dldwd_ev_tick(priv, hw);
+		if (events & HERMES_EV_WTERR)
+			__dldwd_ev_wterr(priv, hw);
+		if (events & HERMES_EV_INFDROP)
+			__dldwd_ev_infdrop(priv, hw);
+		if (events & HERMES_EV_INFO)
+			__dldwd_ev_info(priv, hw);
+		if (events & HERMES_EV_RX)
+			__dldwd_ev_rx(priv, hw);
+		if (events & HERMES_EV_TXEXC)
+			__dldwd_ev_txexc(priv, hw);
+		if (events & HERMES_EV_TX)
+			__dldwd_ev_tx(priv, hw);
+		if (events & HERMES_EV_ALLOC)
+			__dldwd_ev_alloc(priv, hw);
+		
+		hermes_write_regn(hw, EVACK, events);
+	}
+
+	clear_bit(DLDWD_STATE_INIRQ, &priv->state);
+}
+
+static void __dldwd_ev_tick(dldwd_priv_t *priv, hermes_t *hw)
+{
+	struct net_device *dev = &priv->ndev;
+
+	printk(KERN_DEBUG "%s: TICK\n", dev->name);
+}
+
+static void __dldwd_ev_wterr(dldwd_priv_t *priv, hermes_t *hw)
+{
+	struct net_device *dev = &priv->ndev;
+
+	/* This seems to happen a fair bit under load, but ignoring it
+	   seems to work fine...*/
+	DEBUG(1, "%s: MAC controller error (WTERR). Ignoring.\n", dev->name);
+}
+
+static void __dldwd_ev_infdrop(dldwd_priv_t *priv, hermes_t *hw)
+{
+	struct net_device *dev = &priv->ndev;
+
+	printk(KERN_WARNING "%s: Information frame lost.\n", dev->name);
+}
+
+static void __dldwd_ev_info(dldwd_priv_t *priv, hermes_t *hw)
+{
+	struct net_device *dev = &priv->ndev;
+
+	DEBUG(3, "%s: Information frame received.\n", dev->name);
+	/* We don't actually do anything about it - we assume the MAC
+	   controller can deal with it */
+}
+
+static void __dldwd_ev_rx(dldwd_priv_t *priv, hermes_t *hw)
+{
+	struct net_device *dev = &priv->ndev;
+	struct net_device_stats *stats = &priv->stats;
+	struct iw_statistics *wstats = &priv->wstats;
+	struct sk_buff *skb = NULL;
+	uint16_t rxfid, status;
+	int length, data_len, data_off;
+	char *p;
+	struct dldwd_frame_hdr hdr;
+	struct ethhdr *eh;
+	int err;
+
+	rxfid = hermes_read_regn(hw, RXFID);
+	DEBUG(3, "__dldwd_ev_rx(): RXFID=0x%04x\n", rxfid);
+
+	/* We read in the entire frame header here. This isn't really
+	   necessary, since we ignore most of it, but it's
+	   conceptually simpler. We can tune this later if
+	   necessary. */
+	err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), rxfid, 0);
+	if (err) {
+		printk(KERN_ERR "%s: error %d reading frame header. "
+		       "Frame dropped.\n", dev->name, err);
+		stats->rx_errors++;
+		goto drop;
+	}
+
+	status = le16_to_cpu(hdr.desc.status);
+	
+	if (status & HERMES_RXSTAT_ERR) {
+		if ((status & HERMES_RXSTAT_ERR) == HERMES_RXSTAT_BADCRC) {
+			stats->rx_crc_errors++;
+			printk(KERN_WARNING "%s: Bad CRC on Rx. Frame dropped.\n",
+			       dev->name);
+			show_rx_frame(&hdr);
+		} else if ((status & HERMES_RXSTAT_ERR)
+			   == HERMES_RXSTAT_UNDECRYPTABLE) {
+			wstats->discard.code++;
+			printk(KERN_WARNING "%s: Undecryptable frame on Rx. Frame dropped.\n",
+			       dev->name);
+		} else {
+			wstats->discard.misc++;
+			printk("%s: Unknown Rx error (0x%x). Frame dropped.\n",
+			       dev->name, status & HERMES_RXSTAT_ERR);
+		}
+		stats->rx_errors++;
+		goto drop;
+	}
+
+	length = le16_to_cpu(hdr.p80211.data_len);
+	/* Yes, you heard right, that's le16. 802.2 and 802.3 are
+	   big-endian, but 802.11 is little-endian believe it or
+	   not. */
+	
+	/* Sanity check */
+	if (length > MAX_FRAME_SIZE) {
+		printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
+		       dev->name, length);
+		stats->rx_length_errors++;
+		stats->rx_errors++;
+		goto drop;
+	}
+
+	/* We need space for the packet data itself, plus an ethernet
+	   header, plus 2 bytes so we can align the IP header on a
+	   32bit boundary, plus 1 byte so we can read in odd length
+	   packets from the card, which has an IO granularity of 16
+	   bits */  
+	skb = dev_alloc_skb(length+ETH_HLEN+2+1);
+	if (!skb) {
+		printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
+		       dev->name);
+		stats->rx_dropped++;
+		goto drop;
+	}
+
+	skb_reserve(skb, 2); /* This way the IP header is aligned */
+
+	/* Handle decapsulation */
+	switch (status & HERMES_RXSTAT_MSGTYPE) {
+		/* These both indicate a SNAP within 802.2 LLC within
+		   802.3 within 802.11 frame which we'll need to
+		   de-encapsulate. IEEE and ISO OSI have a lot to
+		   answer for.  */
+	case HERMES_RXSTAT_1042:
+	case HERMES_RXSTAT_TUNNEL:
+		data_len = length - ENCAPS_OVERHEAD;
+		data_off = sizeof(hdr);
+
+		eh = (struct ethhdr *)skb_put(skb, ETH_HLEN);
+
+		memcpy(eh, &hdr.p8023, sizeof(hdr.p8023));
+		eh->h_proto = hdr.ethertype;
+
+		break;
+
+		/* Otherwise, we just throw the whole thing in, and hope
+		   the protocol layer can deal with it as 802.3 */
+	default:
+		data_len = length;
+		data_off = P8023_OFFSET;
+		break;
+	}
+
+	p = skb_put(skb, data_len);
+	if (hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len),
+			     rxfid, data_off) != 0) {
+		printk(KERN_WARNING "%s: Error reading packet data\n",
+		       dev->name);
+		stats->rx_errors++;
+		goto drop;
+	}
+
+	dev->last_rx = jiffies;
+	skb->dev = dev;
+	skb->protocol = eth_type_trans(skb, dev);
+	skb->ip_summed = CHECKSUM_NONE;
+	
+	
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += length;
+
+	return;
+
+ drop:	
+	if (skb)
+		dev_kfree_skb_irq(skb);
+	return;
+}
+
+static void __dldwd_ev_txexc(dldwd_priv_t *priv, hermes_t *hw)
+{
+	struct net_device *dev = &priv->ndev;
+	struct net_device_stats *stats = &priv->stats;
+
+	printk(KERN_WARNING "%s: Tx error!\n", dev->name);
+
+	netif_wake_queue(dev);
+	stats->tx_errors++;
+}
+
+static void __dldwd_ev_tx(dldwd_priv_t *priv, hermes_t *hw)
+{
+	struct net_device *dev = &priv->ndev;
+	struct net_device_stats *stats = &priv->stats;
+
+	DEBUG(3, "%s: Transmit completed\n", dev->name);
+
+	stats->tx_packets++;
+	netif_wake_queue(dev);
+}
+
+static void __dldwd_ev_alloc(dldwd_priv_t *priv, hermes_t *hw)
+{
+	uint16_t allocfid;
+
+	allocfid = hermes_read_regn(hw, ALLOCFID);
+	DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev.name, allocfid);
+
+	/* For some reason we don't seem to get transmit completed events properly */
+	if (allocfid == priv->txfid)
+		__dldwd_ev_tx(priv, hw);
+
+/* 	hermes_write_regn(hw, ALLOCFID, 0); */
+}
+
+/*
+ * struct net_device methods
+ */
+
+static int dldwd_init(struct net_device *dev)
+{
+	dldwd_priv_t *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	hermes_id_t nickbuf;
+	uint16_t reclen;
+	int len;
+	char *vendor_str;
+	uint32_t firmver;
+
+	TRACE_ENTER("dldwd");
+	
+	dldwd_lock(priv);
+
+	err = hermes_reset(hw);
+	if (err != 0) {
+		printk(KERN_ERR "%s: failed to reset hardware\n", dev->name);
+		goto out;
+	}
+	
+	/* Get the firmware version */
+	err = hermes_read_staidentity(hw, USER_BAP, &priv->firmware_info);
+	if (err) {
+		printk(KERN_WARNING "%s: Error %d reading firmware info. Wildly guessing capabilities...\n",
+		       dev->name, err);
+		memset(&priv->firmware_info, 0, sizeof(priv->firmware_info));
+	}
+
+	firmver = ((uint32_t)priv->firmware_info.major << 16) | priv->firmware_info.minor;
+
+	/* Determine capabilities from the firmware version */
+
+	switch (priv->firmware_info.vendor) {
+	case 0x1:
+		/* Lucent Wavelan IEEE, Orinoco or Cabletron card */
+		vendor_str = "Lucent";
+
+		priv->broken_reset = 0;
+		priv->has_port3 = 1;
+		priv->has_ibss = (firmver >= 0x60006);
+		priv->has_wep = (firmver >= 0x40020);
+		priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
+					  Gold cards from the others? */
+		priv->wep_type = WEP_TYPE_LUCENT;
+		priv->has_mwo = (firmver >= 0x60000);
+		priv->has_pm = (firmver >= 0x40020);
+		break;
+	case 0x2:
+		vendor_str = "Generic Prism II";
+
+		priv->broken_reset = 0;
+		priv->has_port3 = 1; /* FIXME: no idea if this is right */
+		priv->has_ibss = 0; /* FIXME: no idea if this is right */
+		priv->has_wep = 1;
+		priv->has_big_wep = 0;
+		priv->wep_type = WEP_TYPE_PRISM;
+		priv->has_mwo = 0;
+		priv->has_pm = 1;
+		break;
+	case 0x6:
+		vendor_str = "LinkSys";
+
+		priv->broken_reset = 0;
+		priv->has_port3 = 1;
+		priv->has_ibss = 0;
+		priv->has_wep = 1;
+		priv->has_big_wep = 0;
+		priv->wep_type = WEP_TYPE_PRISM;
+		priv->has_mwo = 0;
+		priv->has_pm = 1;
+		break;
+	default:
+		vendor_str = "UNKNOWN";
+
+		priv->broken_reset = 0;
+		priv->has_port3 = 0;
+		priv->has_ibss = 0;
+		priv->has_wep = 0;
+		priv->has_big_wep = 0;
+		priv->wep_type = 0;
+		priv->has_mwo = 0;
+		priv->has_pm = 0;
+	}
+
+	printk(KERN_INFO "%s: Firmware ID %02X vendor 0x%x (%s) version %d.%02d\n",
+	       dev->name, priv->firmware_info.id, priv->firmware_info.vendor,
+	       vendor_str, priv->firmware_info.major, priv->firmware_info.minor);
+	
+	if (priv->has_port3)
+		printk(KERN_INFO "%s: Lucent ad-hoc demo mode supported.\n", dev->name);
+	if (priv->has_ibss)
+		printk(KERN_INFO "%s: IEEE standard IBSS ad-hoc mode supported.\n",
+		       dev->name);
+	if (priv->has_wep) {
+		printk(KERN_INFO "%s: WEP supported, ", dev->name);
+		if (priv->has_big_wep)
+			printk("\"128\"-bit key.\n");
+		else
+			printk("40-bit key.");
+	}
+
+	/* Get the MAC address */
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNF_MACADDR,
+			      ETH_ALEN, NULL, dev->dev_addr);
+	if (err) {
+		printk(KERN_WARNING "%s: failed to read MAC address!\n",
+		       dev->name);
+		goto out;
+	}
+
+	printk(KERN_INFO "%s: MAC address %02X:%02X:%02X:%02X:%02X:%02X\n",
+	       dev->name, dev->dev_addr[0], dev->dev_addr[1],
+	       dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4],
+	       dev->dev_addr[5]);
+
+	/* Get the station name */
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNF_NICKNAME,
+			      sizeof(nickbuf), &reclen, &nickbuf);
+	if (err) {
+		printk(KERN_ERR "%s: failed to read station name!n",
+		       dev->name);
+		goto out;
+	}
+	if ( nickbuf.len )
+		len = MIN(IW_ESSID_MAX_SIZE, le16_to_cpu(nickbuf.len));
+	else
+		len = MIN(IW_ESSID_MAX_SIZE, 2 * reclen);
+	memcpy(priv->nick, &nickbuf.val, len);
+	priv->nick[len] = '\0';
+
+	printk(KERN_INFO "%s: Station name \"%s\"\n", dev->name, priv->nick);
+
+	/* Get allowed channels */
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNEL_LIST, &priv->channel_mask);
+	if (err) {
+		printk(KERN_ERR "%s: failed to read channel list!\n",
+		       dev->name);
+		goto out;
+	}
+
+	/* Get initial AP density */
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, &priv->ap_density);
+	if (err) {
+		printk(KERN_ERR "%s: failed to read AP density!\n", dev->name);
+		goto out;
+	}
+
+	/* Get initial RTS threshold */
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_RTS_THRESH, &priv->rts_thresh);
+	if (err) {
+		printk(KERN_ERR "%s: failed to read RTS threshold!\n", dev->name);
+		goto out;
+	}
+
+	/* Get initial fragmentation settings */
+	if (priv->has_mwo)
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_MWO_ROBUST,
+					  &priv->mwo_robust);
+	else
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_FRAG_THRESH,
+					  &priv->frag_thresh);
+	if (err) {
+		printk(KERN_ERR "%s: failed to read fragmentation settings!\n", dev->name);
+		goto out;
+	}
+
+	/* Set initial bitrate control*/
+	priv->tx_rate_ctrl = 3;
+
+	/* Power management setup */
+	if (priv->has_pm) {
+		priv->pm_on = 0;
+		priv->pm_mcast = 1;
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD,
+					  &priv->pm_period);
+		if (err) {
+			printk(KERN_ERR "%s: failed to read power management period!\n",
+			       dev->name);
+			goto out;
+		}
+	}
+		
+	/* Set up the default configuration */
+	priv->iw_mode = IW_MODE_INFRA;
+	/* By default use IEEE/IBSS ad-hoc mode if we have it */
+	priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss);
+	set_port_type(priv);
+
+	priv->promiscuous = 0;
+	priv->allmulti = 0;
+	priv->wep_on = 0;
+	priv->tx_key = 0;
+
+	printk(KERN_INFO "%s: ready\n", dev->name);
+
+ out:
+	dldwd_unlock(priv);
+
+	TRACE_EXIT("dldwd");
+
+	return err;
+}
+
+static int dldwd_open(struct net_device *dev)
+{
+	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	dev_link_t *link = &priv->link;
+	int err = 0;
+	
+	TRACE_ENTER(dev->name);
+
+	link->open++;
+	MOD_INC_USE_COUNT;
+	netif_device_attach(dev);
+	
+	err = dldwd_reset(priv);
+	if (err)
+		dldwd_stop(dev);
+	else
+		netif_start_queue(dev);
+
+	TRACE_EXIT(dev->name);
+
+	return err;
+}
+
+static int dldwd_stop(struct net_device *dev)
+{
+	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	dev_link_t *link = &priv->link;
+
+	TRACE_ENTER(dev->name);
+
+	netif_stop_queue(dev);
+
+	dldwd_shutdown(priv);
+	
+	link->open--;
+
+	if (link->state & DEV_STALE_CONFIG)
+		mod_timer(&link->release, jiffies + HZ/20);
+	
+	MOD_DEC_USE_COUNT;
+
+	TRACE_EXIT(dev->name);
+	
+	return 0;
+}
+
+static struct net_device_stats *dldwd_get_stats(struct net_device *dev)
+{
+	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	
+	return &priv->stats;
+}
+
+static struct iw_statistics *dldwd_get_wireless_stats(struct net_device *dev)
+{
+	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	hermes_t *hw = &priv->hw;
+	struct iw_statistics *wstats = &priv->wstats;
+	int err = 0;
+	hermes_commsqual_t cq;
+
+	dldwd_lock(priv);
+
+	if (priv->port_type == 3) {
+		memset(&wstats->qual, 0, sizeof(wstats->qual));
+	} else {
+		err = hermes_read_commsqual(hw, USER_BAP, &cq);
+		
+		wstats->qual.qual = MAX(MIN(cq.qual, 0x8b-0x2f), 0);
+		wstats->qual.level = MAX(MIN(cq.signal, 0x8a), 0x2f) - 0x95;
+		wstats->qual.noise = MAX(MIN(cq.noise, 0x8a), 0x2f) - 0x95;
+		wstats->qual.updated = 7;
+	}
+
+	dldwd_unlock(priv);
+
+	if (err)
+		return NULL;
+		
+	return wstats;
+}
+
+struct p8022_hdr encaps_hdr = {
+	0xaa, 0xaa, 0x03, {0x00, 0x00, 0xf8}
+};
+
+static int dldwd_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct net_device_stats *stats = &priv->stats;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	uint16_t txfid = priv->txfid;
+	char *p;
+	struct ethhdr *eh;
+	int len, data_len, data_off;
+	struct dldwd_frame_hdr hdr;
+	hermes_response_t resp;
+
+	if (! netif_running(dev)) {
+		printk(KERN_ERR "%s: Tx on stopped device!\n",
+		       dev->name);
+		return 1;
+
+	}
+	
+	if (netif_queue_stopped(dev)) {
+		printk(KERN_ERR "%s: Tx while transmitter busy!\n", 
+		       dev->name);
+		return 1;
+	}
+	
+	dldwd_lock(priv);
+
+	/* Length of the packet body */
+	len = MAX(skb->len - ETH_HLEN, ETH_ZLEN);
+
+	eh = (struct ethhdr *)skb->data;
+
+	/* Build the IEEE 802.11 header */
+	memset(&hdr, 0, sizeof(hdr));
+	memcpy(hdr.p80211.addr1, eh->h_dest, ETH_ALEN);
+	memcpy(hdr.p80211.addr2, eh->h_source, ETH_ALEN);
+	hdr.p80211.frame_ctl = DLDWD_FTYPE_DATA;
+
+	/* Encapsulate Ethernet-II frames */
+	if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
+		data_len = len;
+		data_off = sizeof(hdr);
+		p = skb->data + ETH_HLEN;
+
+		/* 802.11 header */
+		hdr.p80211.data_len = cpu_to_le16(data_len + ENCAPS_OVERHEAD);
+
+		/* 802.3 header */
+		memcpy(hdr.p8023.h_dest, eh->h_dest, ETH_ALEN);
+		memcpy(hdr.p8023.h_source, eh->h_source, ETH_ALEN);
+		hdr.p8023.h_proto = htons(data_len + ENCAPS_OVERHEAD);
+		
+		/* 802.2 header */
+		memcpy(&hdr.p8022, &encaps_hdr, sizeof(encaps_hdr));
+
+		hdr.ethertype = eh->h_proto;
+		err  = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
+					 txfid, 0);
+		if (err) {
+			printk(KERN_ERR
+			       "%s: Error %d writing packet header to BAP\n",
+			       dev->name, err);
+			stats->tx_errors++;
+			goto fail;
+		}
+	} else { /* IEEE 802.3 frame */
+		data_len = len + ETH_HLEN;
+		data_off = P8023_OFFSET;
+		p = skb->data;
+		
+		/* 802.11 header */
+		hdr.p80211.data_len = cpu_to_le16(len);
+		err = hermes_bap_pwrite(hw, USER_BAP, &hdr, P8023_OFFSET,
+					txfid, 0);
+		if (err) {
+			printk(KERN_ERR
+			       "%s: Error %d writing packet header to BAP\n",
+			       dev->name, err);
+			stats->tx_errors++;
+			goto fail;
+		}
+	}
+
+	/* Round up for odd length packets */
+	err = hermes_bap_pwrite(hw, USER_BAP, p, RUP_EVEN(data_len), txfid, data_off);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d writing packet data to BAP\n",
+		       dev->name, err);
+		stats->tx_errors++;
+		goto fail;
+	}
+	
+
+	/* Finally, we actually initiate the send */
+	err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, &resp);
+	if (err) {
+		printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err);
+		stats->tx_errors++;
+		goto fail;
+	}
+
+	dev->trans_start = jiffies;
+	stats->tx_bytes += data_off + data_len;
+
+	netif_stop_queue(dev);
+
+	dldwd_unlock(priv);
+
+	dev_kfree_skb(skb);
+
+	return 0;
+ fail:
+
+	dldwd_unlock(priv);
+	return err;
+}
+
+static void dldwd_tx_timeout(struct net_device *dev)
+{
+	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct net_device_stats *stats = &priv->stats;
+	int err = 0;
+
+	printk(KERN_WARNING "%s: Tx timeout! Resetting card.\n", dev->name);
+
+	stats->tx_errors++;
+
+	err = dldwd_reset(priv);
+	if (err)
+		printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n",
+		       dev->name, err);
+	else {
+		dev->trans_start = jiffies;
+		netif_wake_queue(dev);
+	}
+}
+
+static int dldwd_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int err = 0;
+	int ptype;
+	struct iw_range range;
+	int numrates;
+	int i, k;
+
+	TRACE_ENTER(dev->name);
+
+	err = verify_area(VERIFY_WRITE, rrq->pointer, sizeof(range));
+	if (err)
+		return err;
+
+	rrq->length = sizeof(range);
+
+	dldwd_lock(priv);
+	ptype = priv->port_type;
+	dldwd_unlock(priv);
+
+	memset(&range, 0, sizeof(range));
+
+	/* Much of this shamelessly taken from wvlan_cs.c. No idea
+	 * what it all means -dgibson */
+	range.throughput = 0.5 * 1024 * 1024; /* TCP throughput measured with socklib.
+					       I'm hoping MB/s are the right units. */
+	range.min_nwid = range.max_nwid = 0; /* We don't use nwids */
+
+	
+	range.num_channels = NUM_CHANNELS;
+	if (err)
+		return err;
+	k = 0;
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		if (priv->channel_mask & (1 << i)) {
+			range.freq[k].i = i + 1;
+			range.freq[k].m = channel_frequency[i] * 100000;
+			range.freq[k].e = 1;
+			k++;
+		}
+		
+		if (k >= IW_MAX_FREQUENCIES)
+			break;
+	}
+	range.num_frequency = k;
+
+	range.sensitivity = 3;
+
+	if (ptype == 3) {
+		/* Quality stats meaningless in ad-hoc mode */
+		range.max_qual.qual = 0;
+		range.max_qual.level = 0;
+		range.max_qual.noise = 0;
+	} else {
+		range.max_qual.qual = 0x8b - 0x2f;
+		range.max_qual.level = 0x2f - 0x95 - 1;
+		range.max_qual.noise = 0x2f - 0x95 - 1;
+	}
+
+	err = dldwd_hw_get_bitratelist(priv, &numrates,
+				       range.bitrate, IW_MAX_BITRATES);
+	if (err)
+		return err;
+	range.num_bitrates = numrates;
+	
+	range.min_rts = 0;
+	range.max_rts = 2347;
+	range.min_frag = 256;
+	range.max_frag = 2346;
+
+	dldwd_lock(priv);
+	if (priv->has_wep) {
+		range.max_encoding_tokens = MAX_KEYS;
+
+		range.encoding_size[0] = SMALL_KEY_SIZE;
+		range.num_encoding_sizes = 1;
+
+		if (priv->has_big_wep) {
+			range.encoding_size[1] = LARGE_KEY_SIZE;
+			range.num_encoding_sizes = 2;
+		}
+	} else {
+		range.num_encoding_sizes = 0;
+		range.max_encoding_tokens = 0;
+	}
+	dldwd_unlock(priv);
+		
+	range.min_pmp = 0;
+	range.max_pmp = 65535000;
+	range.pmp_flags = IW_POWER_PERIOD;
+	range.pmt_flags = 0;
+	range.pm_capa = IW_POWER_PERIOD | IW_POWER_UNICAST_R;
+
+	range.num_txpower = 0;
+	range.txpower[0] = 15; /* 15dBm */
+	range.txpower_capa = IW_TXPOW_DBM;
+
+	if (copy_to_user(rrq->pointer, &range, sizeof(range)))
+		return -EFAULT;
+
+	TRACE_EXIT(dev->name);
+
+	return 0;
+}
+
+static int dldwd_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int index = (erq->flags & IW_ENCODE_INDEX) - 1;
+	int enable = 0;
+	uint16_t xlen = 0;
+	int err = 0;
+	char keybuf[MAX_KEY_SIZE];
+
+	if (erq->flags & IW_ENCODE_RESTRICTED)
+		return -EINVAL;
+
+	if (erq->pointer) {
+		/* We actually have a key to set */
+		
+		if (copy_from_user(keybuf, erq->pointer, erq->length))
+			return -EFAULT;
+	}
+	
+	dldwd_lock(priv);
+	
+	if (erq->pointer) {
+		if (erq->length > MAX_KEY_SIZE) {
+			err = -E2BIG;
+			goto out;
+		}
+		
+		if ( (erq->length > LARGE_KEY_SIZE)
+		     || ( ! priv->has_big_wep && (erq->length > SMALL_KEY_SIZE))  ) {
+			err = -EINVAL;
+			goto out;
+		}
+		
+		if ((index < 0) || (index >= MAX_KEYS))
+			index = priv->tx_key;
+		
+		if (erq->length > SMALL_KEY_SIZE) {
+			xlen = LARGE_KEY_SIZE;
+		} else if (erq->length > 0) {
+			xlen = SMALL_KEY_SIZE;
+		} else
+			xlen = 0;
+		
+		if ( (index == priv->tx_key) && (xlen > 0) )
+			enable = 1;
+	} else {
+		if ((index < 0) || (index >= MAX_KEYS)) {
+			err = -EINVAL;
+			goto out;
+		}
+		
+		enable = 1;
+	}
+	
+	if (erq->flags & IW_ENCODE_DISABLED)
+		enable = 0;
+	
+	if (erq->pointer) {
+		priv->keys[index].len = cpu_to_le16(xlen);
+		memset(priv->keys[index].data, 0, sizeof(priv->keys[index].data));
+		memcpy(priv->keys[index].data, keybuf, erq->length);
+	}
+	priv->tx_key = index;
+	priv->wep_on = enable;
+	
+ out:
+	dldwd_unlock(priv);
+
+	return 0;
+}
+
+static int dldwd_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int index = (erq->flags & IW_ENCODE_INDEX) - 1;
+	uint16_t xlen = 0;
+	char keybuf[MAX_KEY_SIZE];
+
+	
+	dldwd_lock(priv);
+
+	if ((index < 0) || (index >= MAX_KEYS))
+		index = priv->tx_key;
+
+	erq->flags = 0;
+	if (! priv->wep_on)
+		erq->flags |= IW_ENCODE_DISABLED;
+	erq->flags |= index + 1;
+	
+	xlen = le16_to_cpu(priv->keys[index].len);
+
+	erq->length = xlen;
+
+	if (erq->pointer) {
+		memcpy(keybuf, priv->keys[index].data, MAX_KEY_SIZE);
+	}
+	
+	dldwd_unlock(priv);
+
+	if (erq->pointer) {
+		if (copy_to_user(erq->pointer, keybuf, xlen))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int dldwd_ioctl_setessid(struct net_device *dev, struct iw_point *erq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	char essidbuf[IW_ESSID_MAX_SIZE+1];
+
+	memset(&essidbuf, 0, sizeof(essidbuf));
+
+	if (erq->flags) { 
+		if (erq->length > IW_ESSID_MAX_SIZE)
+			return -E2BIG;
+		
+		if (copy_from_user(&essidbuf, erq->pointer, erq->length))
+			return -EFAULT;
+
+		essidbuf[erq->length] = '\0';
+	}
+
+	dldwd_lock(priv);
+
+	memcpy(priv->desired_essid, essidbuf, IW_ESSID_MAX_SIZE+1);
+
+	dldwd_unlock(priv);
+
+	return 0;
+}
+
+static int dldwd_ioctl_getessid(struct net_device *dev, struct iw_point *erq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	char essidbuf[IW_ESSID_MAX_SIZE+1];
+	int active;
+	int err = 0;
+
+	TRACE_ENTER(dev->name);
+
+	err = dldwd_hw_get_essid(priv, &active, essidbuf);
+	if (err)
+		return err;
+
+	erq->flags = 1;
+	erq->length = strlen(essidbuf) + 1;
+	if (erq->pointer)
+		if ( copy_to_user(erq->pointer, essidbuf, erq->length) )
+			return -EFAULT;
+
+	TRACE_EXIT(dev->name);
+	
+	return 0;
+}
+
+static int dldwd_ioctl_setnick(struct net_device *dev, struct iw_point *nrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	char nickbuf[IW_ESSID_MAX_SIZE+1];
+
+	if (nrq->length > IW_ESSID_MAX_SIZE)
+		return -E2BIG;
+
+	memset(nickbuf, 0, sizeof(nickbuf));
+
+	if (copy_from_user(nickbuf, nrq->pointer, nrq->length))
+		return -EFAULT;
+
+	nickbuf[nrq->length] = '\0';
+	
+	dldwd_lock(priv);
+
+	memcpy(priv->nick, nickbuf, sizeof(priv->nick));
+
+	dldwd_unlock(priv);
+
+	return 0;
+}
+
+static int dldwd_ioctl_getnick(struct net_device *dev, struct iw_point *nrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	char nickbuf[IW_ESSID_MAX_SIZE+1];
+
+	dldwd_lock(priv);
+	memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1);
+	dldwd_unlock(priv);
+
+	nrq->length = strlen(nickbuf)+1;
+
+	if (copy_to_user(nrq->pointer, nickbuf, sizeof(nickbuf)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int dldwd_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int chan = -1;
+
+	if (priv->iw_mode == IW_MODE_ADHOC)
+		return -EOPNOTSUPP;
+
+	if ( (frq->e == 0) && (frq->m <= 1000) ) {
+		/* Setting by channel number */
+		chan = frq->m;
+	} else {
+		/* Setting by frequency - search the table */
+		int mult = 1;
+		int i;
+
+		for (i = 0; i < (6 - frq->e); i++)
+			mult *= 10;
+
+		for (i = 0; i < NUM_CHANNELS; i++)
+			if (frq->m == (channel_frequency[i] * mult))
+				chan = i+1;
+	}
+
+	if ( (chan < 1) || (chan > NUM_CHANNELS) ||
+	     ! (priv->channel_mask & (1 << (chan-1)) ) )
+		return -EINVAL;
+
+	dldwd_lock(priv);
+	priv->channel = chan;
+	dldwd_unlock(priv);
+
+	return 0;
+}
+
+static int dldwd_ioctl_getsens(struct net_device *dev, struct iw_param *srq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	uint16_t val;
+	int err;
+
+	dldwd_lock(priv);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, &val);
+	dldwd_unlock(priv);
+
+	if (err)
+		return err;
+
+	srq->value = val;
+	srq->fixed = 0; /* auto */
+
+	return 0;
+}
+
+static int dldwd_ioctl_setsens(struct net_device *dev, struct iw_param *srq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int val = srq->value;
+
+	if ((val < 1) || (val > 3))
+		return -EINVAL;
+	
+	dldwd_lock(priv);
+	priv->ap_density = val;
+	dldwd_unlock(priv);
+
+	return 0;
+}
+
+static int dldwd_ioctl_setrts(struct net_device *dev, struct iw_param *rrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int val = rrq->value;
+
+	if (rrq->disabled)
+		val = 2347;
+
+	if ( (val < 0) || (val > 2347) )
+		return -EINVAL;
+
+	dldwd_lock(priv);
+	priv->rts_thresh = val;
+	dldwd_unlock(priv);
+
+	return 0;
+}
+
+static int dldwd_ioctl_setfrag(struct net_device *dev, struct iw_param *frq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int err = 0;
+
+	dldwd_lock(priv);
+
+	if (priv->has_mwo) {
+		if (frq->disabled)
+			priv->mwo_robust = 0;
+		else {
+			if (frq->fixed)
+				printk(KERN_WARNING "%s: Fixed fragmentation not \
+supported on this firmware. Using MWO robust instead.\n", dev->name);
+			priv->mwo_robust = 1;
+		}
+	} else {
+		if (frq->disabled)
+			priv->frag_thresh = 2346;
+		else {
+			if ( (frq->value < 256) || (frq->value > 2346) )
+				err = -EINVAL;
+			else
+				priv->frag_thresh = frq->value & ~0x1; /* must be even */
+		}
+	}
+
+	dldwd_unlock(priv);
+
+	return err;
+}
+
+static int dldwd_ioctl_getfrag(struct net_device *dev, struct iw_param *frq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	uint16_t val;
+
+	dldwd_lock(priv);
+	
+	if (priv->has_mwo) {
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_MWO_ROBUST, &val);
+		if (err)
+			val = 0;
+
+		frq->value = val ? 2347 : 0;
+		frq->disabled = ! val;
+		frq->fixed = 0;
+	} else {
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_FRAG_THRESH, &val);
+		if (err)
+			val = 0;
+
+		frq->value = val;
+		frq->disabled = (val >= 2346);
+		frq->fixed = 1;
+	}
+
+	dldwd_unlock(priv);
+	
+	return err;
+}
+
+static int dldwd_ioctl_setrate(struct net_device *dev, struct iw_param *rrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int err = 0;
+	int rate_ctrl = -1;
+	int brate;
+	int i;
+
+	dldwd_lock(priv);
+
+	brate = rrq->value / 500000;
+	if (! rrq->fixed) {
+		if (brate > 0)
+			brate = -brate;
+		else
+			brate = -22;
+	}
+	
+	for (i = 0; i < NUM_RATES; i++)
+		if (rate_list[i] == brate) {
+			rate_ctrl = i;
+			break;
+		}
+	
+	if ( (rate_ctrl < 1) || (rate_ctrl >= NUM_RATES) )
+		err = -EINVAL;
+	else
+		priv->tx_rate_ctrl = rate_ctrl;
+
+	dldwd_unlock(priv);
+
+	return err;
+}
+
+static int dldwd_ioctl_getrate(struct net_device *dev, struct iw_param *rrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	uint16_t val;
+	int brate;
+
+	dldwd_lock(priv);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_RATE_CTRL, &val);
+	if (err)
+		goto out;
+	
+	brate = rate_list[val];
+	
+	if (brate < 0) {
+		rrq->fixed = 0;
+
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_TX_RATE, &val);
+		if (err)
+			goto out;
+
+		if (val == 6)
+			brate = 11;
+		else
+			brate = 2*val;
+	} else
+		rrq->fixed = 1;
+
+	rrq->value = brate * 500000;
+	rrq->disabled = 0;
+
+ out:
+	dldwd_unlock(priv);
+
+	return err;
+}
+
+static int dldwd_ioctl_setpower(struct net_device *dev, struct iw_param *prq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int err = 0;
+	int mcast = 1;
+
+
+	dldwd_lock(priv);
+
+	if (prq->disabled) {
+		priv->pm_on = 0;
+	} else {
+		switch (prq->flags & IW_POWER_MODE) {
+		case IW_POWER_UNICAST_R:
+			mcast = 0;
+			break;
+		case IW_POWER_ALL_R:
+			mcast = 1;
+			break;
+		case IW_POWER_ON:
+			mcast = priv->pm_mcast;
+			break;
+		default:
+			err = -EINVAL;
+		}
+		if (err)
+			goto out;
+		
+		if (prq->flags & IW_POWER_TIMEOUT) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		if (prq->flags & IW_POWER_PERIOD) {
+			priv->pm_on = 1;
+			priv->pm_mcast = mcast;
+			priv->pm_period = prq->value / 1000;
+		} else {
+			err = -EINVAL;
+			goto out;
+		}			
+
+	}
+
+ out:
+	dldwd_unlock(priv);
+
+	return err;
+}
+
+static int dldwd_ioctl_getpower(struct net_device *dev, struct iw_param *prq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	uint16_t enable, period, mcast;
+
+	dldwd_lock(priv);
+	
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_ENABLE, &enable);
+	if (err)
+		goto out;
+
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD, &period);
+	if (err)
+		goto out;
+
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_MCAST_RX, &mcast);
+	if (err)
+		goto out;
+
+	prq->disabled = !enable;
+	prq->flags = IW_POWER_PERIOD;
+	if (mcast)
+		prq->flags |= IW_POWER_ALL_R;
+	else
+		prq->flags |= IW_POWER_UNICAST_R;
+	prq->value = period * 1000;
+
+ out:
+	dldwd_unlock(priv);
+
+	return err;
+}
+
+static int dldwd_ioctl_setport3(struct net_device *dev, struct iwreq *wrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int val = *( (int *) wrq->u.name );
+	int err = 0;
+
+	dldwd_lock(priv);
+	switch (val) {
+	case 0: /* Try to do IEEE ad-hoc mode */
+		if (! priv->has_ibss) {
+			err = -EINVAL;
+			break;
+		}
+		priv->prefer_port3 = 0;
+			
+		break;
+
+	case 1: /* Try to do Lucent proprietary ad-hoc mode */
+		if (! priv->has_port3) {
+			err = -EINVAL;
+			break;
+		}
+		priv->prefer_port3 = 1;
+		break;
+
+	default:
+		err = -EINVAL;
+	}
+
+	dldwd_unlock(priv);
+
+	return err;
+}
+
+static int dldwd_ioctl_getport3(struct net_device *dev, struct iwreq *wrq)
+{
+	dldwd_priv_t *priv = dev->priv;
+	int *val = (int *)wrq->u.name;
+
+	dldwd_lock(priv);
+	*val = priv->prefer_port3;
+	dldwd_unlock(priv);
+
+	return 0;
+}
+
+static int dldwd_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	dldwd_priv_t *priv = dev->priv;
+	struct iwreq *wrq = (struct iwreq *)rq;
+	int err = 0;
+	int changed = 0;
+
+	TRACE_ENTER(dev->name);
+
+	switch (cmd) {
+	case SIOCGIWNAME:
+		DEBUG(1, "%s: SIOCGIWNAME\n", dev->name);
+		strcpy(wrq->u.name, "IEEE 802.11-DS");
+		break;
+		
+	case SIOCGIWAP:
+		DEBUG(1, "%s: SIOCGIWAP\n", dev->name);
+		wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
+		err = dldwd_hw_get_bssid(priv, wrq->u.ap_addr.sa_data);
+		break;
+
+	case SIOCGIWRANGE:
+		DEBUG(1, "%s: SIOCGIWRANGE\n", dev->name);
+		err = dldwd_ioctl_getiwrange(dev, &wrq->u.data);
+		break;
+
+	case SIOCSIWMODE:
+		DEBUG(1, "%s: SIOCSIWMODE\n", dev->name);
+		dldwd_lock(priv);
+		switch (wrq->u.mode) {
+		case IW_MODE_ADHOC:
+			if (! (priv->has_ibss || priv->has_port3) )
+				err = -EINVAL;
+			else {
+				priv->iw_mode = IW_MODE_ADHOC;
+				changed = 1;
+			}
+			break;
+
+		case IW_MODE_INFRA:
+			priv->iw_mode = IW_MODE_INFRA;
+			changed = 1;
+			break;
+
+		default:
+			err = -EINVAL;
+			break;
+		}
+		set_port_type(priv);
+		dldwd_unlock(priv);
+		break;
+
+	case SIOCGIWMODE:
+		DEBUG(1, "%s: SIOCGIWMODE\n", dev->name);
+		dldwd_lock(priv);
+		wrq->u.mode = priv->iw_mode;
+		dldwd_unlock(priv);
+		break;
+
+	case SIOCSIWENCODE:
+		DEBUG(1, "%s: SIOCSIWENCODE\n", dev->name);
+		if (! priv->has_wep) {
+			err = -EOPNOTSUPP;
+			break;
+		}
+
+		err = dldwd_ioctl_setiwencode(dev, &wrq->u.encoding);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWENCODE:
+		DEBUG(1, "%s: SIOCGIWENCODE\n", dev->name);
+		if (! priv->has_wep) {
+			err = -EOPNOTSUPP;
+			break;
+		}
+
+		if (! capable(CAP_NET_ADMIN)) {
+			err = -EPERM;
+			break;
+		}
+
+		err = dldwd_ioctl_getiwencode(dev, &wrq->u.encoding);
+		break;
+
+	case SIOCSIWESSID:
+		DEBUG(1, "%s: SIOCSIWESSID\n", dev->name);
+		err = dldwd_ioctl_setessid(dev, &wrq->u.essid);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWESSID:
+		DEBUG(1, "%s: SIOCGIWESSID\n", dev->name);
+		err = dldwd_ioctl_getessid(dev, &wrq->u.essid);
+		break;
+
+	case SIOCSIWNICKN:
+		DEBUG(1, "%s: SIOCSIWNICKN\n", dev->name);
+		err = dldwd_ioctl_setnick(dev, &wrq->u.data);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWNICKN:
+		DEBUG(1, "%s: SIOCGIWNICKN\n", dev->name);
+		err = dldwd_ioctl_getnick(dev, &wrq->u.data);
+		break;
+
+	case SIOCGIWFREQ:
+		DEBUG(1, "%s: SIOCGIWFREQ\n", dev->name);
+		wrq->u.freq.m = dldwd_hw_get_freq(priv);
+		wrq->u.freq.e = 1;
+		break;
+
+	case SIOCSIWFREQ:
+		DEBUG(1, "%s: SIOCSIWFREQ\n", dev->name);
+		err = dldwd_ioctl_setfreq(dev, &wrq->u.freq);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWSENS:
+		DEBUG(1, "%s: SIOCGIWSENS\n", dev->name);
+		err = dldwd_ioctl_getsens(dev, &wrq->u.sens);
+		break;
+
+	case SIOCSIWSENS:
+		DEBUG(1, "%s: SIOCSIWSENS\n", dev->name);
+		err = dldwd_ioctl_setsens(dev, &wrq->u.sens);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWRTS:
+		DEBUG(1, "%s: SIOCGIWRTS\n", dev->name);
+		wrq->u.rts.value = priv->rts_thresh;
+		wrq->u.rts.disabled = (wrq->u.rts.value == 2347);
+		wrq->u.rts.fixed = 1;
+		break;
+
+	case SIOCSIWRTS:
+		DEBUG(1, "%s: SIOCSIWRTS\n", dev->name);
+		err = dldwd_ioctl_setrts(dev, &wrq->u.rts);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCSIWFRAG:
+		DEBUG(1, "%s: SIOCSIWFRAG\n", dev->name);
+		err = dldwd_ioctl_setfrag(dev, &wrq->u.frag);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWFRAG:
+		DEBUG(1, "%s: SIOCGIWFRAG\n", dev->name);
+		err = dldwd_ioctl_getfrag(dev, &wrq->u.frag);
+		break;
+
+	case SIOCSIWRATE:
+		DEBUG(1, "%s: SIOCSIWRATE\n", dev->name);
+		err = dldwd_ioctl_setrate(dev, &wrq->u.bitrate);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWRATE:
+		DEBUG(1, "%s: SIOCGIWRATE\n", dev->name);
+		err = dldwd_ioctl_getrate(dev, &wrq->u.bitrate);
+		break;
+
+	case SIOCSIWPOWER:
+		DEBUG(1, "%s: SIOCSIWPOWER\n", dev->name);
+		err = dldwd_ioctl_setpower(dev, &wrq->u.power);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCGIWPOWER:
+		DEBUG(1, "%s: SIOCGIWPOWER\n", dev->name);
+		err = dldwd_ioctl_getpower(dev, &wrq->u.power);
+		break;
+
+	case SIOCGIWTXPOW:
+		DEBUG(1, "%s: SIOCGIWTXPOW\n", dev->name);
+		/* The card only supports one tx power, so this is easy */
+		wrq->u.txpower.value = 15; /* dBm */
+		wrq->u.txpower.fixed = 1;
+		wrq->u.txpower.disabled = 0;
+		wrq->u.txpower.flags = IW_TXPOW_DBM;
+		break;
+
+	case SIOCGIWPRIV:
+		DEBUG(1, "%s: SIOCGIWPRIV\n", dev->name);
+		if (wrq->u.data.pointer) {
+			struct iw_priv_args privtab[] = {
+				{ SIOCDEVPRIVATE + 0x0, 0, 0, "force_reset" },
+				{ SIOCDEVPRIVATE + 0x2,
+				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+				  0, "set_port3" },
+				{ SIOCDEVPRIVATE + 0x3, 0,
+				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+				  "get_port3" }
+			};
+
+			err = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(privtab));
+			if (err)
+				break;
+			
+			wrq->u.data.length = sizeof(privtab) / sizeof(privtab[0]);
+			if (copy_to_user(wrq->u.data.pointer, privtab, sizeof(privtab)))
+				err = -EFAULT;
+		}
+		break;
+	       
+	case SIOCDEVPRIVATE + 0x0: /* force_reset */
+		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x0 (force_reset)\n",
+		      dev->name);
+		if (! capable(CAP_NET_ADMIN)) {
+			err = -EPERM;
+			break;
+		}
+		
+		printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
+		dldwd_reset(priv);
+		break;
+
+	case SIOCDEVPRIVATE + 0x2: /* set_port3 */
+		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x2 (set_port3)\n",
+		      dev->name);
+		if (! capable(CAP_NET_ADMIN)) {
+			err = -EPERM;
+			break;
+		}
+
+		err = dldwd_ioctl_setport3(dev, wrq);
+		if (! err)
+			changed = 1;
+		break;
+
+	case SIOCDEVPRIVATE + 0x3: /* get_port3 */
+		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x3 (get_port3)\n",
+		      dev->name);
+		err = dldwd_ioctl_getport3(dev, wrq);
+		break;
+
+	default:
+		err = -EOPNOTSUPP;
+	}
+	
+	if (! err && changed && netif_running(dev)) {
+		err = dldwd_reset(priv);
+		if (err)
+			dldwd_stop(dev);
+	}		
+
+	TRACE_EXIT(dev->name);
+		
+	return err;
+}
+
+static int dldwd_change_mtu(struct net_device *dev, int new_mtu)
+{
+	TRACE_ENTER(dev->name);
+
+	if ( (new_mtu < DLDWD_MIN_MTU) || (new_mtu > DLDWD_MAX_MTU) )
+		return -EINVAL;
+
+	dev->mtu = new_mtu;
+
+	TRACE_EXIT(dev->name);
+
+	return 0;
+}
+
+static void __dldwd_set_multicast_list(struct net_device *dev)
+{
+	dldwd_priv_t *priv = dev->priv;
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+	int promisc, allmulti, mc_count;
+
+	TRACE_ENTER(dev->name);
+
+	DEBUG(3, "dev->flags=0x%x, priv->promiscuous=%d, dev->mc_count=%d priv->mc_count=%d\n",
+	      dev->flags, priv->promiscuous, dev->mc_count, priv->mc_count);
+
+	/* The Hermes doesn't seem to have an allmulti mode, so we go
+	 * into promiscuous mode and let the upper levels deal. */
+	if ( (dev->flags & IFF_PROMISC) ) {
+		promisc = 1;
+		allmulti = 0;
+		mc_count = 0;
+	} else if ( (dev->flags & IFF_ALLMULTI) ||
+		    (dev->mc_count > HERMES_MAX_MULTICAST) ) {
+		promisc = 0;
+		allmulti = 1;
+		mc_count = HERMES_MAX_MULTICAST;
+	} else {
+		promisc = 0;
+		allmulti = 0;
+		mc_count = dev->mc_count;
+	}
+
+	DEBUG(3, "promisc=%d mc_count=%d\n",
+	      promisc, mc_count);
+
+	if (promisc != priv->promiscuous) { /* Don't touch the hardware if we don't have to */
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PROMISCUOUS,
+					   promisc);
+		if (err) {
+			printk(KERN_ERR "%s: Error %d setting promiscuity to %d.\n",
+			       dev->name, err, promisc);
+		} else 
+			priv->promiscuous = promisc;
+	}
+
+	if (allmulti) {
+		/* FIXME: This method of doing allmulticast reception
+		   comes from the NetBSD driver. Haven't actually
+		   tested whether it works or not. */
+		hermes_multicast_t mclist;
+
+		memset(&mclist, 0, sizeof(mclist));
+		err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_MULTICAST_LIST, &mclist);
+		if (err)
+			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
+			       dev->name, err);
+		else
+			priv->allmulti = 1;
+		       
+	} else if (mc_count || (! mc_count && priv->mc_count) ) {
+		struct dev_mc_list *p = dev->mc_list;
+		hermes_multicast_t mclist;
+		int i;
+
+		for (i = 0; i < mc_count; i++) {
+			/* First some paranoid checks */
+			if (! p) {
+				printk(KERN_ERR "%s: Multicast list shorter than mc_count.\n",
+				       dev->name);
+				break;
+			}
+			if (p->dmi_addrlen != ETH_ALEN) {
+
+				printk(KERN_ERR "%s: Bad address size (%d) in multicast list.\n",
+				       dev->name, p->dmi_addrlen);
+				break;
+			}
+
+			memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
+			p = p->next;
+		}
+
+		/* More paranoia */
+		if (p)
+			printk(KERN_ERR "%s: Multicast list longer than mc_count.\n",
+			       dev->name);
+
+		priv->mc_count = i;			
+
+		DEBUG(3, "priv->mc_count = %d\n", priv->mc_count);
+
+		err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNF_MULTICAST_LIST,
+				       HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN),
+				       &mclist);
+		if (err)
+			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
+			       dev->name, err);
+		else
+			priv->allmulti = 0;
+	}
+
+	/* Since we can set the promiscuous flag when it wasn't asked
+	   for, make sure the net_device knows about it. */
+	if (priv->promiscuous)
+		dev->flags |= IFF_PROMISC;
+	else
+		dev->flags &= ~IFF_PROMISC;
+
+	if (priv->allmulti)
+		dev->flags |= IFF_ALLMULTI;
+	else
+		dev->flags &= ~IFF_ALLMULTI;
+
+	TRACE_EXIT(dev->name);
+}
+
+/*
+ * procfs stuff
+ */
+
+static struct proc_dir_entry *dir_base = NULL;
+
+/*
+ * This function updates the total amount of data printed so far. It then
+ * determines if the amount of data printed into a buffer  has reached the
+ * offset requested. If it hasn't, then the buffer is shifted over so that
+ * the next bit of data can be printed over the old bit. If the total
+ * amount printed so far exceeds the total amount requested, then this
+ * function returns 1, otherwise 0.
+ */
+static int 
+
+shift_buffer(char *buffer, int requested_offset, int requested_len,
+	     int *total, int *slop, char **buf)
+{
+	int printed;
+	
+	printed = *buf - buffer;
+	if (*total + printed <= requested_offset) {
+		*total += printed;
+		*buf = buffer;
+	}
+	else {
+		if (*total < requested_offset) {
+			*slop = requested_offset - *total;
+		}
+		*total = requested_offset + printed - *slop;
+	}
+	if (*total > requested_offset + requested_len) {
+		return 1;
+	}
+	else {
+		return 0;
+	}
+}
+
+/*
+ * This function calculates the actual start of the requested data
+ * in the buffer. It also calculates actual length of data returned,
+ * which could be less that the amount of data requested.
+ */
+#define PROC_BUFFER_SIZE 4096
+#define PROC_SAFE_SIZE 3072
+
+static int
+calc_start_len(char *buffer, char **start, int requested_offset,
+	       int requested_len, int total, char *buf)
+{
+	int return_len, buffer_len;
+	
+	buffer_len = buf - buffer;
+	if (buffer_len >= PROC_BUFFER_SIZE - 1) {
+		printk(KERN_ERR "calc_start_len: exceeded /proc buffer size\n");
+	}
+	
+	/*
+	 * There may be bytes before and after the
+	 * chunk that was actually requested.
+	 */
+	return_len = total - requested_offset;
+	if (return_len < 0) {
+		return_len = 0;
+	}
+	*start = buf - return_len;
+	if (return_len > requested_len) {
+		return_len = requested_len;
+	}
+	return return_len;
+}
+
+static int
+dldwd_proc_get_hermes_regs(char *page, char **start, off_t requested_offset,
+			   int requested_len, int *eof, void *data)
+{
+	dldwd_priv_t *dev = (dldwd_priv_t *)data;
+	hermes_t *hw = &dev->hw;
+	char *buf;
+	int total = 0, slop = 0;
+
+	buf = page;
+
+#define DHERMESREG(name) buf += sprintf(buf, "%-16s: %04x\n", #name, hermes_read_regn(hw, name))
+
+	DHERMESREG(CMD);
+	DHERMESREG(PARAM0);
+	DHERMESREG(PARAM1);
+	DHERMESREG(PARAM2);
+	DHERMESREG(STATUS);
+	DHERMESREG(RESP0);
+	DHERMESREG(RESP1);
+	DHERMESREG(RESP2);
+	DHERMESREG(INFOFID);
+	DHERMESREG(RXFID);
+	DHERMESREG(ALLOCFID);
+	DHERMESREG(TXCOMPLFID);
+	DHERMESREG(SELECT0);
+	DHERMESREG(OFFSET0);
+	DHERMESREG(SELECT1);
+	DHERMESREG(OFFSET1);
+	DHERMESREG(EVSTAT);
+	DHERMESREG(INTEN);
+	DHERMESREG(EVACK);
+	DHERMESREG(CONTROL);
+	DHERMESREG(SWSUPPORT0);
+	DHERMESREG(SWSUPPORT1);
+	DHERMESREG(SWSUPPORT2);
+	DHERMESREG(AUXPAGE);
+	DHERMESREG(AUXOFFSET);
+	DHERMESREG(AUXDATA);
+#undef DHERMESREG
+
+	shift_buffer(page, requested_offset, requested_len, &total,
+		     &slop, &buf);
+	return calc_start_len(page, start, requested_offset, requested_len,
+			      total, buf);
+}
+
+struct {
+	uint16_t rid;
+	char *name;
+	int minlen, maxlen;
+	int displaytype;
+#define DISPLAY_WORDS	0
+#define DISPLAY_BYTES	1
+#define DISPLAY_STRING	2
+} record_table[] = {
+#define RTCNFENTRY(name, type) { HERMES_RID_CNF_##name, #name, 0, LTV_BUF_SIZE, type }
+	RTCNFENTRY(PORTTYPE, DISPLAY_WORDS),
+	RTCNFENTRY(MACADDR, DISPLAY_BYTES),
+	RTCNFENTRY(DESIRED_SSID, DISPLAY_STRING),
+	RTCNFENTRY(CHANNEL, DISPLAY_WORDS),
+	RTCNFENTRY(OWN_SSID, DISPLAY_STRING),
+	RTCNFENTRY(SYSTEM_SCALE, DISPLAY_WORDS),
+	RTCNFENTRY(MAX_DATA_LEN, DISPLAY_WORDS),
+	RTCNFENTRY(PM_ENABLE, DISPLAY_WORDS),
+	RTCNFENTRY(PM_MCAST_RX, DISPLAY_WORDS),
+	RTCNFENTRY(PM_PERIOD, DISPLAY_WORDS),
+	RTCNFENTRY(NICKNAME, DISPLAY_STRING),
+	RTCNFENTRY(WEP_ON, DISPLAY_WORDS),
+	RTCNFENTRY(MWO_ROBUST, DISPLAY_WORDS),
+	RTCNFENTRY(MULTICAST_LIST, DISPLAY_BYTES),
+	RTCNFENTRY(CREATEIBSS, DISPLAY_WORDS),
+	RTCNFENTRY(FRAG_THRESH, DISPLAY_WORDS),
+	RTCNFENTRY(RTS_THRESH, DISPLAY_WORDS),
+	RTCNFENTRY(TX_RATE_CTRL, DISPLAY_WORDS),
+	RTCNFENTRY(PROMISCUOUS, DISPLAY_WORDS),
+	RTCNFENTRY(KEYS, DISPLAY_BYTES),
+	RTCNFENTRY(TX_KEY, DISPLAY_WORDS),
+	RTCNFENTRY(TICKTIME, DISPLAY_WORDS),
+	RTCNFENTRY(PRISM2_TX_KEY, DISPLAY_WORDS),
+	RTCNFENTRY(PRISM2_KEY0, DISPLAY_BYTES),
+	RTCNFENTRY(PRISM2_KEY1, DISPLAY_BYTES),
+	RTCNFENTRY(PRISM2_KEY2, DISPLAY_BYTES),
+	RTCNFENTRY(PRISM2_KEY3, DISPLAY_BYTES),
+	RTCNFENTRY(PRISM2_WEP_ON, DISPLAY_WORDS),
+#undef RTCNFENTRY
+#define RTINFENTRY(name,type) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, type }
+	RTINFENTRY(CHANNEL_LIST, DISPLAY_WORDS),
+	RTINFENTRY(STAIDENTITY, DISPLAY_WORDS),
+	RTINFENTRY(CURRENT_SSID, DISPLAY_STRING),
+	RTINFENTRY(CURRENT_BSSID, DISPLAY_BYTES),
+	RTINFENTRY(COMMSQUALITY, DISPLAY_WORDS),
+	RTINFENTRY(CURRENT_TX_RATE, DISPLAY_WORDS),
+	RTINFENTRY(WEP_AVAIL, DISPLAY_WORDS),
+	RTINFENTRY(CURRENT_CHANNEL, DISPLAY_WORDS),
+	RTINFENTRY(DATARATES, DISPLAY_BYTES),
+#undef RTINFENTRY
+};
+#define NUM_RIDS ( sizeof(record_table) / sizeof(record_table[0]) )
+
+static int
+dldwd_proc_get_hermes_recs(char *page, char **start, off_t requested_offset,
+			   int requested_len, int *eof, void *data)
+{
+	dldwd_priv_t *dev = (dldwd_priv_t *)data;
+	hermes_t *hw = &dev->hw;
+	char *buf;
+	int total = 0, slop = 0;
+	int i;
+	uint16_t length;
+	int err;
+
+	buf = page;
+
+	/* print out all the config RIDs */
+	for (i = 0; i < NUM_RIDS; i++) {
+		uint16_t rid = record_table[i].rid;
+		int minlen = record_table[i].minlen;
+		int maxlen = record_table[i].maxlen;
+		int len;
+		uint8_t *val8;
+		uint16_t *val16;
+		int j;
+
+		val8 = kmalloc(maxlen + 2, GFP_KERNEL);
+		if (! val8)
+			return -ENOMEM;
+
+		err = hermes_read_ltv(hw, USER_BAP, rid, maxlen,
+				      &length, val8);
+		if (err) {
+			DEBUG(0, "Error %d reading RID 0x%04x\n", err, rid);
+			continue;
+		}
+		val16 = (uint16_t *)val8;
+
+		buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name,
+			       rid, length, (length-1)*2);
+		len = MIN( MAX(minlen, (length-1)*2), maxlen);
+
+		switch (record_table[i].displaytype) {
+		case DISPLAY_WORDS:
+			for (j = 0; j < len / 2; j++) {
+				buf += sprintf(buf, "%04X-", le16_to_cpu(val16[j]));
+			}
+			buf--;
+			break;
+
+		case DISPLAY_BYTES:
+		default:
+			for (j = 0; j < len; j++) {
+				buf += sprintf(buf, "%02X:", val8[j]);
+			}
+			buf--;
+			break;
+
+		case DISPLAY_STRING:
+			len = MIN(len, le16_to_cpu(val16[0])+2);
+			val8[len] = '\0';
+			buf += sprintf(buf, "\"%s\"", (char *)&val16[1]);
+			break;
+		}
+
+		buf += sprintf(buf, "\n");
+
+		kfree(val8);
+
+		if (shift_buffer(page, requested_offset, requested_len,
+				 &total, &slop, &buf))
+			break;
+
+		if ( (buf - page) > PROC_SAFE_SIZE )
+			break;
+	}
+
+	return calc_start_len(page, start, requested_offset, requested_len,
+			      total, buf);
+}
+
+/* initialise the /proc subsystem for the hermes driver, creating the
+ * separate entries */
+static int
+dldwd_proc_init(void)
+{
+	int err = 0;
+
+	TRACE_ENTER("dldwd");
+
+	/* create the directory for it to sit in */
+	dir_base = create_proc_entry("dldwd", S_IFDIR, &proc_root);
+	if (dir_base == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/dldwd.\n");
+		dldwd_proc_cleanup();
+		err = -ENOMEM;
+	}
+
+	TRACE_EXIT("dldwd");
+
+	return err;
+}
+
+static int
+dldwd_proc_dev_init(dldwd_priv_t *dev)
+{
+	dev->dir_dev = NULL;
+	/* create the directory for it to sit in */
+	dev->dir_dev = create_proc_entry(dev->node.dev_name, S_IFDIR | S_IRUGO | S_IXUGO,
+					 dir_base);
+	if (dev->dir_dev == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/dldwd/%s.\n",  dev->node.dev_name);
+		goto fail;
+	}
+
+	dev->dir_regs = NULL;
+	dev->dir_regs = create_proc_read_entry("regs", S_IFREG | S_IRUGO,
+					       dev->dir_dev, dldwd_proc_get_hermes_regs, dev);
+	if (dev->dir_regs == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/dldwd/%s/regs.\n",  dev->node.dev_name);
+		goto fail;
+	}
+
+	dev->dir_recs = NULL;
+	dev->dir_recs = create_proc_read_entry("recs", S_IFREG | S_IRUGO,
+					       dev->dir_dev, dldwd_proc_get_hermes_recs, dev);
+	if (dev->dir_recs == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/dldwd/%s/recs.\n",  dev->node.dev_name);
+		goto fail;
+	}
+
+	return 0;
+ fail:
+	dldwd_proc_dev_cleanup(dev);
+	return -ENOMEM;
+}
+
+static void
+dldwd_proc_dev_cleanup(dldwd_priv_t *priv)
+{
+	if (priv->dir_regs) {
+		remove_proc_entry("regs", priv->dir_dev);
+		priv->dir_regs = NULL;
+	}		
+	if (priv->dir_recs) {
+		remove_proc_entry("recs", priv->dir_dev);
+		priv->dir_recs = NULL;
+	}		
+	if (priv->dir_dev) {
+		remove_proc_entry(priv->node.dev_name, dir_base);
+		priv->dir_dev = NULL;
+	}
+}
+
+static void
+dldwd_proc_cleanup(void)
+{
+	TRACE_ENTER("dldwd");
+
+	if (dir_base) {
+		remove_proc_entry("dldwd", &proc_root);
+		dir_base = NULL;
+	}
+	
+	TRACE_EXIT("dldwd");
+}
+
+/*====================================================================*/
+
+/*
+ * From here on in, it's all PCMCIA junk, taken almost directly from
+ * dummy_cs.c
+ */
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "dldwd_cs";
+
+/*
+   A linked list of "instances" of the dummy device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+static int num_instances = 0;
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+	CardServices(ReportError, handle, &err);
+}
+
+/*======================================================================
+  dldwd_attach() creates an "instance" of the driver, allocating
+  local data structures for one device.  The device is registered
+  with Card Services.
+  
+  The dev_link structure is initialized, but we don't actually
+  configure the card at this point -- we wait until we receive a
+  card insertion event.
+  ======================================================================*/
+
+static dev_link_t *dldwd_attach(void)
+{
+	dldwd_priv_t *priv;
+	dev_link_t *link;
+	struct net_device *ndev;
+	client_reg_t client_reg;
+	int ret, i;
+
+	TRACE_ENTER("dldwd");
+
+	/* Allocate space for private device-specific data */
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (! priv) {
+		link = NULL;
+		goto out;
+	}
+
+	memset(priv, 0, sizeof(*priv));
+	priv->instance = num_instances++; /* FIXME: Racy? */
+	spin_lock_init(&priv->lock);
+
+	link = &priv->link;
+	ndev = &priv->ndev;
+	link->priv = ndev->priv = priv;
+
+	/* Initialize the dev_link_t structure */
+	link->release.function = &dldwd_release;
+	link->release.data = (u_long) link;
+
+	/* Interrupt setup */
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+	if (irq_list[0] == -1)
+		link->irq.IRQInfo2 = irq_mask;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << irq_list[i];
+	link->irq.Handler = NULL;
+
+	/*
+	   General socket configuration defaults can go here.  In this
+	   client, we assume very little, and rely on the CIS for almost
+	   everything.  In most clients, many details (i.e., number, sizes,
+	   and attributes of IO windows) are fixed by the nature of the
+	   device, and can be hard-wired here.
+	 */
+	link->conf.Attributes = 0;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+
+	/* Set up the net_device */
+	ether_setup(ndev);
+	ndev->init = dldwd_init;
+	ndev->open = dldwd_open;
+	ndev->stop = dldwd_stop;
+	ndev->hard_start_xmit = dldwd_xmit;
+	ndev->tx_timeout = dldwd_tx_timeout;
+	ndev->watchdog_timeo = 4*HZ; /* 4 second timeout */
+
+	ndev->get_stats = dldwd_get_stats;
+	ndev->get_wireless_stats = dldwd_get_wireless_stats;
+
+	ndev->do_ioctl = dldwd_ioctl;
+	
+	ndev->change_mtu = dldwd_change_mtu;
+	ndev->set_multicast_list = dldwd_set_multicast_list;
+
+	netif_stop_queue(ndev);
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask =
+	    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.event_handler = &dldwd_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		dldwd_detach(link);
+		link = NULL;
+		goto out;
+	}
+
+ out:
+	TRACE_EXIT("dldwd");
+	return link;
+}				/* dldwd_attach */
+
+/*======================================================================
+  This deletes a driver "instance".  The device is de-registered
+  with Card Services.  If it has been released, all local data
+  structures are freed.  Otherwise, the structures will be freed
+  when the device is released.
+  ======================================================================*/
+
+static void dldwd_detach(dev_link_t * link)
+{
+	dev_link_t **linkp;
+	dldwd_priv_t *priv = link->priv;
+
+	TRACE_ENTER("dldwd");
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+	if (*linkp == NULL)
+		goto out;
+
+	/*
+	   If the device is currently configured and active, we won't
+	   actually delete it yet.  Instead, it is marked so that when
+	   the release() function is called, that will trigger a proper
+	   detach().
+	 */
+	if (link->state & DEV_CONFIG) {
+#ifdef PCMCIA_DEBUG
+		printk(KERN_DEBUG "dldwd_cs: detach postponed, '%s' "
+		       "still locked\n", link->dev->dev_name);
+#endif
+		link->state |= DEV_STALE_LINK;
+		goto out;
+	}
+
+	/* Break the link with Card Services */
+	if (link->handle)
+		CardServices(DeregisterClient, link->handle);
+
+	/* Unlink device structure, and free it */
+	*linkp = link->next;
+	DEBUG(0, "dldwd_cs: detach: link=%p link->dev=%p\n", link, link->dev);
+	if (link->dev) {
+		DEBUG(0, "dldwd_cs: About to unregister net device %p\n",
+		      &priv->ndev);
+		unregister_netdev(&priv->ndev);
+	}
+	kfree(priv);
+
+	num_instances--; /* FIXME: Racy? */
+
+ out:
+	TRACE_EXIT("dldwd");
+}				/* dldwd_detach */
+
+/*======================================================================
+  dldwd_config() is scheduled to run after a CARD_INSERTION event
+  is received, to configure the PCMCIA socket, and to make the
+  device available to the system.
+  ======================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed
+
+#define CFG_CHECK(fn, args...) \
+if (CardServices(fn, args) != 0) goto next_entry
+
+static void dldwd_config(dev_link_t * link)
+{
+	client_handle_t handle = link->handle;
+	dldwd_priv_t *priv = link->priv;
+	hermes_t *hw = &priv->hw;
+	struct net_device *ndev = &priv->ndev;
+	tuple_t tuple;
+	cisparse_t parse;
+	int last_fn, last_ret;
+	u_char buf[64];
+	config_info_t conf;
+	cisinfo_t info;
+
+	TRACE_ENTER("dldwd");
+
+	CS_CHECK(ValidateCIS, handle, &info);
+
+	/*
+	   This reads the card's CONFIG tuple to find its configuration
+	   registers.
+	 */
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	tuple.Attributes = 0;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = sizeof(buf);
+	tuple.TupleOffset = 0;
+	CS_CHECK(GetFirstTuple, handle, &tuple);
+	CS_CHECK(GetTupleData, handle, &tuple);
+	CS_CHECK(ParseTuple, handle, &tuple, &parse);
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+
+	/* Look up the current Vcc */
+	CS_CHECK(GetConfigurationInfo, handle, &conf);
+	link->conf.Vcc = conf.Vcc;
+
+	DEBUG(0, "dldwd_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", 
+	      link->conf.ConfigBase, link->conf.Vcc);
+
+	/*
+	   In this loop, we scan the CIS for configuration table entries,
+	   each of which describes a valid card configuration, including
+	   voltage, IO window, memory window, and interrupt settings.
+
+	   We make no assumptions about the card to be configured: we use
+	   just the information available in the CIS.  In an ideal world,
+	   this would work for any PCMCIA card, but it requires a complete
+	   and accurate CIS.  In practice, a driver usually "knows" most of
+	   these things without consulting the CIS, and most client drivers
+	   will only use the CIS to fill in implementation-defined details.
+	 */
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	CS_CHECK(GetFirstTuple, handle, &tuple);
+	while (1) {
+		cistpl_cftable_entry_t dflt = { 0 };
+		cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+		CFG_CHECK(GetTupleData, handle, &tuple);
+		CFG_CHECK(ParseTuple, handle, &tuple, &parse);
+
+		DEBUG(0, "dldwd_config: index = 0x%x, flags = 0x%x\n",
+		      cfg->index, cfg->flags);
+
+		if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+			dflt = *cfg;
+		if (cfg->index == 0)
+			goto next_entry;
+		link->conf.ConfigIndex = cfg->index;
+
+		/* Does this card need audio output? */
+		if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+			link->conf.Attributes |= CONF_ENABLE_SPKR;
+			link->conf.Status = CCSR_AUDIO_ENA;
+		}
+
+		/* Use power settings for Vcc and Vpp if present */
+		/*  Note that the CIS values need to be rescaled */
+		if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+			if (conf.Vcc !=
+			    cfg->vcc.param[CISTPL_POWER_VNOM] /
+			    10000) goto next_entry;
+		} else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
+			if (conf.Vcc !=
+			    dflt.vcc.param[CISTPL_POWER_VNOM] /
+			    10000) goto next_entry;
+		}
+
+		if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+			link->conf.Vpp1 = link->conf.Vpp2 =
+			    cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+		else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
+			link->conf.Vpp1 = link->conf.Vpp2 =
+			    dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
+		
+		DEBUG(0, "dldwd_config: We seem to have configured Vcc and Vpp\n");
+
+		/* Do we need to allocate an interrupt? */
+		if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+			link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+		/* IO window settings */
+		link->io.NumPorts1 = link->io.NumPorts2 = 0;
+		if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+			cistpl_io_t *io =
+			    (cfg->io.nwin) ? &cfg->io : &dflt.io;
+			link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+			if (!(io->flags & CISTPL_IO_8BIT))
+				link->io.Attributes1 =
+				    IO_DATA_PATH_WIDTH_16;
+			if (!(io->flags & CISTPL_IO_16BIT))
+				link->io.Attributes1 =
+				    IO_DATA_PATH_WIDTH_8;
+			link->io.IOAddrLines =
+			    io->flags & CISTPL_IO_LINES_MASK;
+			link->io.BasePort1 = io->win[0].base;
+			link->io.NumPorts1 = io->win[0].len;
+			if (io->nwin > 1) {
+				link->io.Attributes2 =
+				    link->io.Attributes1;
+				link->io.BasePort2 = io->win[1].base;
+				link->io.NumPorts2 = io->win[1].len;
+			}
+
+			/* This reserves IO space but doesn't actually enable it */
+			CFG_CHECK(RequestIO, link->handle, &link->io);
+		}
+
+
+		/* If we got this far, we're cool! */
+
+		break;
+		
+	next_entry:
+		if (link->io.NumPorts1)
+			CardServices(ReleaseIO, link->handle, &link->io);
+		CS_CHECK(GetNextTuple, handle, &tuple);
+	}
+
+	/*
+	   Allocate an interrupt line.  Note that this does not assign a
+	   handler to the interrupt, unless the 'Handler' member of the
+	   irq structure is initialized.
+	 */
+	if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+		int i;
+
+		link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+		link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+		if (irq_list[0] == -1)
+			link->irq.IRQInfo2 = irq_mask;
+		else
+			for (i=0; i<4; i++)
+				link->irq.IRQInfo2 |= 1 << irq_list[i];
+		
+  		link->irq.Handler = dldwd_interrupt; 
+  		link->irq.Instance = priv; 
+		
+		CS_CHECK(RequestIRQ, link->handle, &link->irq);
+	}
+
+	sprintf(priv->node.dev_name, "dldwd%d", priv->instance);
+	
+	/* We initialize the hermes structure before completing PCMCIA
+	   configuration just in case the interrupt handler gets
+	   called. */
+	hermes_struct_init(hw, link->io.BasePort1);
+
+	/*
+	   This actually configures the PCMCIA socket -- setting up
+	   the I/O windows and the interrupt mapping, and putting the
+	   card and host interface into "Memory and IO" mode.
+	 */
+	CS_CHECK(RequestConfiguration, link->handle, &link->conf);
+
+	/* Finally, report what we've done */
+	printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+	       priv->node.dev_name, link->conf.ConfigIndex,
+	       link->conf.Vcc / 10, link->conf.Vcc % 10);
+	if (link->conf.Vpp1)
+		printk(", Vpp %d.%d", link->conf.Vpp1 / 10,
+		       link->conf.Vpp1 % 10);
+	if (link->conf.Attributes & CONF_ENABLE_IRQ)
+		printk(", irq %d", link->irq.AssignedIRQ);
+	if (link->io.NumPorts1)
+		printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+		       link->io.BasePort1 + link->io.NumPorts1 - 1);
+	if (link->io.NumPorts2)
+		printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+		       link->io.BasePort2 + link->io.NumPorts2 - 1);
+	printk("\n");
+	
+	/* And give us the proc nodes for debugging */
+	if (dldwd_proc_dev_init(priv) != 0) {
+		printk(KERN_ERR "dldwd_cs: Failed to create /proc node for %s\n",
+		       priv->node.dev_name);
+		goto failed;
+	}
+	
+	ndev->base_addr = link->io.BasePort1;
+	ndev->irq = link->irq.AssignedIRQ;
+
+	strcpy(ndev->name, priv->node.dev_name);
+	if (register_netdev(ndev) != 0) {
+		printk(KERN_ERR "dldwd_cs: register_netdev() failed\n");
+		goto failed;
+	}
+
+	/*
+	   At this point, the dev_node_t structure(s) need to be
+	   initialized and arranged in a linked list at link->dev.
+	 */
+	priv->node.major = priv->node.minor = 0;
+	link->dev = &priv->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+
+	TRACE_EXIT("dldwd");
+
+	return;
+
+ cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+ failed:
+	dldwd_release((u_long) link);
+
+	TRACE_EXIT("dldwd");
+}				/* dldwd_config */
+
+/*======================================================================
+  After a card is removed, dldwd_release() will unregister the
+  device, and release the PCMCIA configuration.  If the device is
+  still open, this will be postponed until it is closed.
+  ======================================================================*/
+
+static void dldwd_release(u_long arg)
+{
+	dev_link_t *link = (dev_link_t *) arg;
+	dldwd_priv_t *priv = link->priv;
+
+	TRACE_ENTER(link->dev->dev_name);
+
+	/*
+	   If the device is currently in use, we won't release until it
+	   is actually closed, because until then, we can't be sure that
+	   no one will try to access the device or its data structures.
+	 */
+	if (link->open) {
+		DEBUG(0, "dldwd_cs: release postponed, '%s' still open\n",
+		      link->dev->dev_name);
+		link->state |= DEV_STALE_CONFIG;
+		return;
+	}
+
+	/*
+	   In a normal driver, additional code may be needed to release
+	   other kernel data structures associated with this device. 
+	 */
+
+	dldwd_proc_dev_cleanup(priv);
+
+	/* Don't bother checking to see if these succeed or not */
+	CardServices(ReleaseConfiguration, link->handle);
+	if (link->io.NumPorts1)
+		CardServices(ReleaseIO, link->handle, &link->io);
+	if (link->irq.AssignedIRQ)
+		CardServices(ReleaseIRQ, link->handle, &link->irq);
+	link->state &= ~DEV_CONFIG;
+
+	TRACE_EXIT(link->dev->dev_name);
+}				/* dldwd_release */
+
+/*======================================================================
+  The card status event handler.  Mostly, this schedules other
+  stuff to run after an event is received.
+
+  When a CARD_REMOVAL event is received, we immediately set a
+  private flag to block future accesses to this device.  All the
+  functions that actually access the device should check this flag
+  to make sure the card is still present.
+  ======================================================================*/
+
+static int dldwd_event(event_t event, int priority,
+		       event_callback_args_t * args)
+{
+	dev_link_t *link = args->client_data;
+	dldwd_priv_t *priv = (dldwd_priv_t *)link->priv;
+	struct net_device *dev = &priv->ndev;
+
+	TRACE_ENTER("dldwd");
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		dldwd_shutdown(priv);
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			netif_stop_queue(dev);
+			netif_device_detach(dev);
+			mod_timer(&link->release, jiffies + HZ / 20);
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		dldwd_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		dldwd_shutdown(priv);
+		/* Mark the device as stopped, to block IO until later */
+
+		if (link->state & DEV_CONFIG) {
+			if (link->open) {
+				netif_stop_queue(dev);
+				netif_device_detach(dev);
+			}
+			CardServices(ReleaseConfiguration, link->handle);
+		}
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (link->state & DEV_CONFIG) {
+			CardServices(RequestConfiguration, link->handle,
+				     &link->conf);
+
+			if (link->open) {
+				if (dldwd_reset(priv) == 0) {
+					netif_device_attach(dev);
+					netif_start_queue(dev);
+				} else {
+					printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n",
+					       dev->name);
+					dldwd_stop(dev);
+				}
+			}
+		}
+		/*
+		   In a normal driver, additional code may go here to restore
+		   the device state and restart IO. 
+		 */
+		break;
+	}
+
+	TRACE_EXIT("dldwd");
+
+	return 0;
+}				/* dldwd_event */
+
+static int __init init_dldwd_cs(void)
+{
+	servinfo_t serv;
+	int err;
+	
+	TRACE_ENTER("dldwd");
+
+	printk(KERN_INFO "dldwd: David's Less Dodgy WaveLAN/IEEE Driver\n");
+
+	DEBUG(0, "%s\n", version);
+	CardServices(GetCardServicesInfo, &serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		printk(KERN_NOTICE "dldwd_cs: Card Services release "
+		       "does not match!\n");
+		return -1;
+	}
+	register_pccard_driver(&dev_info, &dldwd_attach, &dldwd_detach);
+
+	
+	err = dldwd_proc_init();
+
+	TRACE_EXIT("dldwd");
+	return err;
+}
+
+static void __exit exit_dldwd_cs(void)
+{
+	TRACE_ENTER("dldwd");
+
+	dldwd_proc_cleanup();
+
+	unregister_pccard_driver(&dev_info);
+
+	if (dev_list)
+		DEBUG(0, "dldwd_cs: Removing leftover devices.\n");
+	while (dev_list != NULL) {
+		del_timer(&dev_list->release);
+		if (dev_list->state & DEV_CONFIG)
+			dldwd_release((u_long) dev_list);
+		dldwd_detach(dev_list);
+	}
+
+	TRACE_EXIT("dldwd");
+}
+
+module_init(init_dldwd_cs);
+module_exit(exit_dldwd_cs);
diff -urN linux-2.4.3-pre6/drivers/usb/devio.c linux/drivers/usb/devio.c
--- linux-2.4.3-pre6/drivers/usb/devio.c	Sun Nov 12 20:45:19 2000
+++ linux/drivers/usb/devio.c	Mon Dec  3 22:33:05 2001
@@ -838,7 +838,7 @@
 		}
 		if (totlen > 32768) {
 			kfree(isopkt);
-			return -ENOMEM;
+			return -EINVAL;
 		}
 		uurb.buffer_length = totlen;
 		break;
diff -urN linux-2.4.3-pre6/drivers/usb/hub.c linux/drivers/usb/hub.c
--- linux-2.4.3-pre6/drivers/usb/hub.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/hub.c	Mon Dec  3 22:33:05 2001
@@ -291,6 +291,7 @@
 
 	INIT_LIST_HEAD(&hub->event_list);
 	hub->dev = dev;
+	init_MUTEX(&hub->khubd_sem);
 
 	/* Record the new hub's existence */
 	spin_lock_irqsave(&hub_event_lock, flags);
@@ -334,6 +335,9 @@
 
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 
+	down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */
+	up(&hub->khubd_sem);
+
 	if (hub->urb) {
 		usb_unlink_urb(hub->urb);
 		usb_free_urb(hub->urb);
@@ -545,6 +549,13 @@
 		return;
 	}
 
+	/* Some low speed devices have problems with the quick delay, so */
+	/*  be a bit pessimistic with those devices. RHbug #23670 */
+	if (portstatus & USB_PORT_STAT_LOW_SPEED) {
+		wait_ms(400);
+		delay = HUB_LONG_RESET_TIME;
+	}
+
 	down(&usb_address0_sem);
 
 	tempstr = kmalloc(1024, GFP_KERNEL);
@@ -639,7 +650,7 @@
 		spin_lock_irqsave(&hub_event_lock, flags);
 
 		if (list_empty(&hub_event_list))
-			goto he_unlock;
+			break;
 
 		/* Grab the next entry from the beginning of the list */
 		tmp = hub_event_list.next;
@@ -650,6 +661,7 @@
 		list_del(tmp);
 		INIT_LIST_HEAD(tmp);
 
+		down(&hub->khubd_sem); /* never blocks, we were on list */
 		spin_unlock_irqrestore(&hub_event_lock, flags);
 
 		if (hub->error) {
@@ -658,6 +670,7 @@
 			if (usb_hub_reset(hub)) {
 				err("error resetting hub %d - disconnecting", dev->devnum);
 				usb_hub_disconnect(dev);
+				up(&hub->khubd_sem);
 				continue;
 			}
 
@@ -733,9 +746,9 @@
                         	usb_hub_power_on(hub);
 			}
 		}
+		up(&hub->khubd_sem);
         } /* end while (1) */
 
-he_unlock:
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 }
 
diff -urN linux-2.4.3-pre6/drivers/usb/hub.h linux/drivers/usb/hub.h
--- linux-2.4.3-pre6/drivers/usb/hub.h	Mon Feb 19 17:14:57 2001
+++ linux/drivers/usb/hub.h	Mon Dec  3 22:33:05 2001
@@ -108,6 +108,8 @@
 	int nports;
 
 	struct usb_hub_descriptor *descriptor;
+
+	struct semaphore khubd_sem;
 };
 
 #endif
diff -urN linux-2.4.3-pre6/drivers/usb/microtek.c linux/drivers/usb/microtek.c
--- linux-2.4.3-pre6/drivers/usb/microtek.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/microtek.c	Mon Dec  3 22:33:05 2001
@@ -9,7 +9,7 @@
  * This driver implements a SCSI host controller driver and a USB
  * device driver. To avoid confusion, all the USB related stuff is
  * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
- * 
+ *
  * Microtek (www.microtek.com) did not release the specifications for
  * their USB protocol to us, so we had to reverse engineer them. We
  * don't know for which models they are valid.
@@ -52,7 +52,7 @@
  * you want it, just send mail.
  *
  * Status:
- *	
+ *
  *	Untested with multiple scanners.
  *	Untested on SMP.
  *	Untested on a bigendian machine.
@@ -104,6 +104,17 @@
  *	20000822 Version 0.2.3
  *	20000913 Reduced module size if debugging is off
  *	20000913 Version 0.2.4
+ *      20010210 New abort logic
+ *      20010210 Version 0.3.0
+ *	20010217 Merged scatter/gather
+ *	20010218 Version 0.4.0
+ *	20010218 Cosmetic fixes
+ *	20010218 Version 0.4.1
+ *      20010306 Abort while using scatter/gather
+ *      20010306 Version 0.4.2
+ *      20010311 Remove all timeouts and tidy up generally (john)
+ *	20010320 check return value of scsi_register()
+ *	20010320 Version 0.4.3
  */
 
 #include <linux/module.h>
@@ -128,15 +139,9 @@
 
 #include "microtek.h"
 
-/* Constants */
-
-#define MTS_ABORT_TIMEOUT HZ /*jiffies*/
-
-
 /* Should we do debugging? */
 
-// #define MTS_DO_DEBUG
-
+//#define MTS_DO_DEBUG
 
 /* USB layer driver interface */
 
@@ -147,7 +152,7 @@
 static struct usb_device_id mts_usb_ids [];
 
 static struct usb_driver mts_usb_driver = {
-	name:		"microtek",
+	name:		"microtekX6",
 	probe:		mts_usb_probe,
 	disconnect:	mts_usb_disconnect,
 	id_table:	mts_usb_ids,
@@ -156,7 +161,7 @@
 
 /* Internal driver stuff */
 
-#define MTS_VERSION	"0.2.4"
+#define MTS_VERSION	"0.4.3"
 #define MTS_NAME	"microtek usb (rev " MTS_VERSION "): "
 
 #define MTS_WARNING(x...) \
@@ -177,8 +182,8 @@
 	MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ )
 #define MTS_DEBUG_INT() \
 	do { MTS_DEBUG_GOT_HERE(); \
-	     MTS_DEBUG("transfer = %x context = %x\n",(int)transfer,(int)context ); \
-	     MTS_DEBUG("status = %x data-length = %x sent = %x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
+	     MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
+	     MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
              mts_debug_dump(context->instance);\
 	   } while(0)
 #else
@@ -190,23 +195,17 @@
 #define MTS_DEBUG_INT() MTS_NUL_STATEMENT
 
 #endif
-	
+
 
 
 #define MTS_INT_INIT()\
-	do {\
-	context = (struct mts_transfer_context*)transfer->context; \
-	if (atomic_read(&context->do_abort)) {\
-		mts_transfer_cleanup(transfer);\
-		return;\
-	}\
+	struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; \
 	MTS_DEBUG_INT();\
-	} while (0)
 
 #ifdef MTS_DO_DEBUG
 
 static inline void mts_debug_dump(struct mts_desc* desc) {
-	MTS_DEBUG("desc at 0x%x: halted = %x%x, toggle = %x%x\n",
+	MTS_DEBUG("desc at 0x%x: halted = %02x%02x, toggle = %02x%02x\n",
 		  (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0],
 		  (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
 		);
@@ -296,60 +295,34 @@
 
  out:
 	MTS_DEBUG( "  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-	       srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], 
+	       srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
 	       srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
 }
 
 #else
 
-static inline void mts_show_command(Scsi_Cmnd *srb)
+static inline void mts_show_command(Scsi_Cmnd * dummy)
 {
-	while (0) {}
 }
 
-static inline void mts_debug_dump(struct mts_desc* desc)
+static inline void mts_debug_dump(struct mts_desc* dummy)
 {
-	while (0) {}
 }
 
 #endif
 
 
-
-
-static inline int mts_is_aborting(struct mts_desc* desc) {
+/* static inline int mts_is_aborting(struct mts_desc* desc) {
 	return (atomic_read(&desc->context.do_abort));
-}
-
-static inline void mts_request_abort(struct mts_desc* desc) {
-	MTS_DEBUG_GOT_HERE();
-	mts_debug_dump(desc);
-	atomic_set(&desc->context.do_abort,1);
-}
+}  */
 
 static inline void mts_urb_abort(struct mts_desc* desc) {
 	MTS_DEBUG_GOT_HERE();
 	mts_debug_dump(desc);
-	if ( desc->urb.status == USB_ST_URB_PENDING ) {
-		usb_unlink_urb( &desc->urb );
-	}
-}
-
-static inline void mts_wait_abort(struct mts_desc* desc)
-{
-	mts_request_abort(desc);
-
-	while( !atomic_read(&desc->lock.count) ) {
-/* Is there a function to check if the semaphore is locked? */
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout( MTS_ABORT_TIMEOUT ); 
-		MTS_DEBUG_GOT_HERE();
-		mts_urb_abort(desc);
-	}
 
+	usb_unlink_urb( &desc->urb );
 }
 
-
 static struct mts_desc * mts_list; /* list of active scanners */
 struct semaphore mts_list_semaphore;
 
@@ -362,10 +335,10 @@
 		   (int)to_remove );
 
 	lock_kernel();
-	mts_wait_abort(to_remove);
+	mts_urb_abort(to_remove);
 
 	MTS_DEBUG_GOT_HERE();
-	
+
 	if ( to_remove != mts_list ) {
 		MTS_DEBUG_GOT_HERE();
 		if (to_remove->prev && to_remove->next)
@@ -378,7 +351,7 @@
 			mts_list->prev = 0;
 		}
 	}
-		
+
 	if ( to_remove->next ) {
 		MTS_DEBUG_GOT_HERE();
 		to_remove->next->prev = to_remove->prev;
@@ -387,7 +360,7 @@
 	MTS_DEBUG_GOT_HERE();
 	scsi_unregister_module(MODULE_SCSI_HA, &(to_remove->ctempl));
 	unlock_kernel();
-	
+
 	kfree( to_remove );
 }
 
@@ -395,16 +368,16 @@
 void mts_add_nolock( struct mts_desc* to_add )
 {
 	MTS_DEBUG( "adding 0x%x to list\n", (int)to_add );
-	
+
 	to_add->prev = 0;
 	to_add->next = mts_list;
 	if ( mts_list ) {
 		mts_list->prev = to_add;
 	}
-			
+
 	mts_list = to_add;
 }
-		
+
 
 
 
@@ -415,20 +388,18 @@
 static int mts_scsi_release(struct Scsi_Host *psh)
 {
 	MTS_DEBUG_GOT_HERE();
-	
+
 	return 0;
 }
 
 static int mts_scsi_abort (Scsi_Cmnd *srb)
-/* interrupt context (!) */ /* FIXME this is about to become task context */
 {
 	struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]);
 
 	MTS_DEBUG_GOT_HERE();
-	
-	mts_request_abort(desc);
+
 	mts_urb_abort(desc);
-	
+
 	return SCSI_ABORT_PENDING;
 }
 
@@ -450,10 +421,10 @@
 static int mts_scsi_detect (struct SHT * sht)
 {
 	/* Whole function stolen from usb-storage */
-	
+
 	struct mts_desc * desc = (struct mts_desc *)sht->proc_dir;
 	/* What a hideous hack! */
-	  
+
 	char local_name[48];
 
 	MTS_DEBUG_GOT_HERE();
@@ -467,15 +438,20 @@
 		MTS_ERROR( "unable to allocate memory for proc interface!!\n" );
 		return 0;
 	}
-	
+
 	strcpy(sht->proc_name, local_name);
 
  	sht->proc_dir = NULL;
 
 	/* In host->hostdata we store a pointer to desc */
 	desc->host = scsi_register(sht, sizeof(desc));
+	if (desc->host == NULL) {
+		MTS_ERROR("Cannot register due to low memory");
+		kfree(sht->proc_name);
+		return 0;
+	}
 	desc->host->hostdata[0] = (unsigned long)desc;
-/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */ 
+/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */
 
 	return 1;
 }
@@ -490,6 +466,7 @@
 int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
 
 static void mts_transfer_cleanup( struct urb *transfer );
+static void mts_do_sg(struct urb * transfer);
 
 
 inline static
@@ -503,10 +480,9 @@
 /* Holding transfer->context->lock! */
 {
 	int res;
-	struct mts_transfer_context* context;
-	
+
 	MTS_INT_INIT();
-	
+
 	FILL_BULK_URB(transfer,
 		      context->instance->usb_dev,
 		      pipe,
@@ -516,16 +492,13 @@
 		      context
 		);
 
-/*	transfer->transfer_flags = USB_DISABLE_SPD;*/
 	transfer->transfer_flags = USB_ASYNC_UNLINK;
 	transfer->status = 0;
-	transfer->timeout = 100;
 
 	res = usb_submit_urb( transfer );
 	if ( res ) {
 		MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
 		context->srb->result = DID_ERROR << 16;
-		context->state = mts_con_error;
 		mts_transfer_cleanup(transfer);
 	}
 	return;
@@ -535,8 +508,8 @@
 static void mts_transfer_cleanup( struct urb *transfer )
 /* Interrupt context! */
 {
-	struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context;
-	
+	MTS_INT_INIT();
+
 	if ( context->final_callback )
 		context->final_callback(context->srb);
 	up( &context->instance->lock );
@@ -545,8 +518,6 @@
 
 static void mts_transfer_done( struct urb *transfer )
 {
-	struct mts_transfer_context* context;
-
 	MTS_INT_INIT();
 
 	context->srb->result &= MTS_SCSI_ERR_MASK;
@@ -561,34 +532,25 @@
 static void mts_get_status( struct urb *transfer )
 /* Interrupt context! */
 {
-	struct mts_transfer_context* context;
-
 	MTS_INT_INIT();
 
-	context->state = mts_con_status;
-	
 	mts_int_submit_urb(transfer,
 			   usb_rcvbulkpipe(context->instance->usb_dev,
 					   context->instance->ep_response),
 			   &context->status,
 			   1,
 			   mts_transfer_done );
-	
-	
-	return;
 }
 
 static void mts_data_done( struct urb* transfer )
 /* Interrupt context! */
 {
-	struct mts_transfer_context* context;
-
 	MTS_INT_INIT();
 
 	if ( context->data_length != transfer->actual_length ) {
 		context->srb->resid = context->data_length - transfer->actual_length;
 	} else if ( transfer->status ) {
-		context->srb->result = DID_ERROR<<16;
+		context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
 	}
 
 	mts_get_status(transfer);
@@ -600,39 +562,65 @@
 static void mts_command_done( struct urb *transfer )
 /* Interrupt context! */
 {
-	struct mts_transfer_context* context;
-
 	MTS_INT_INIT();
 
 	if ( transfer->status ) {
-		context->srb->result = DID_ERROR<<16;
+	        if (transfer->status == -ENOENT) {
+		        /* We are being killed */
+			MTS_DEBUG_GOT_HERE();
+			context->srb->result = DID_ABORT<<16;
+                } else {
+		        /* A genuine error has occured */
+			MTS_DEBUG_GOT_HERE();
+
+		        context->srb->result = DID_ERROR<<16;
+                }
 		mts_transfer_cleanup(transfer);
-		
+
 		return;
 	}
-	
+
 	if ( context->data ) {
-		context->state = mts_con_data;
 		mts_int_submit_urb(transfer,
 				   context->data_pipe,
 				   context->data,
 				   context->data_length,
-				   mts_data_done);
+				   context->srb->use_sg ? mts_do_sg : mts_data_done);
 	} else mts_get_status(transfer);
-	
+
 	return;
 }
 
+static void mts_do_sg (struct urb* transfer)
+{
+	struct scatterlist * sg;
+	MTS_INT_INIT();
+	
+	MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,context->srb->use_sg);
+
+	if (transfer->status) {
+                context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
+		mts_transfer_cleanup(transfer);
+        }
 
+	sg = context->srb->buffer;
+	context->fragment++;
+	mts_int_submit_urb(transfer,
+			context->data_pipe,
+			sg[context->fragment].address,
+			sg[context->fragment].length,
+			context->fragment + 1 == context->srb->use_sg ? mts_data_done : mts_do_sg);
+	return;
+}
 
-	static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
-	static const u8 mts_read_image_sig_len = 4;
-	static const unsigned char mts_direction[256/8] = {
-		0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, 
-		0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-	};
+static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
+static const u8 mts_read_image_sig_len = 4;
+static const unsigned char mts_direction[256/8] = {
+	0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+	0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
 
 
 #define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
@@ -640,26 +628,34 @@
 static void
 mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
 {
-
 	int pipe;
-
-
+	struct scatterlist * sg;
+	
 	MTS_DEBUG_GOT_HERE();
 
 	desc->context.instance = desc;
 	desc->context.srb = srb;
-	desc->context.state = mts_con_command;
-	atomic_set(&desc->context.do_abort,0);
-	
-	if ( !srb->bufflen ){
-		desc->context.data = 0;
-		desc->context.data_length = 0;
-		return;
+	desc->context.fragment = 0;
+
+	if (!srb->use_sg) {
+		if ( !srb->bufflen ){
+			desc->context.data = 0;
+			desc->context.data_length = 0;
+			return;
+		} else {
+			desc->context.data = srb->buffer;
+			desc->context.data_length = srb->bufflen;
+			MTS_DEBUG("length = %d or %d\n",
+				  srb->request_bufflen, srb->bufflen);
+		}
 	} else {
-		desc->context.data = srb->buffer;
-		desc->context.data_length = srb->bufflen;
+		MTS_DEBUG("Using scatter/gather\n");
+		sg = srb->buffer;
+		desc->context.data = sg[0].address;
+		desc->context.data_length = sg[0].length;
 	}
-	
+
+
 	/* can't rely on srb->sc_data_direction */
 
 	/* Brutally ripped from usb-storage */
@@ -691,11 +687,11 @@
 	MTS_DEBUG_GOT_HERE();
 	mts_show_command(srb);
 	mts_debug_dump(desc);
-	
+
 	if ( srb->device->lun || srb->device->id || srb->device->channel ) {
 
 		MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel );
-		
+
 		MTS_DEBUG("this device doesn't exist\n");
 
 		srb->result = DID_BAD_TARGET << 16;
@@ -704,14 +700,10 @@
 			callback(srb);
 
 		goto out;
-	}	
+	}
 
 	down(&desc->lock);
-
-	MTS_DEBUG_GOT_HERE();
-	mts_show_command(srb);
 	
-
 	FILL_BULK_URB(&desc->urb,
 		      desc->usb_dev,
 		      usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
@@ -720,31 +712,24 @@
 		      mts_command_done,
 		      &desc->context
 		      );
-	
-		
+
+
 	mts_build_transfer_context( srb, desc );
 	desc->context.final_callback = callback;
-	desc->urb.timeout = 100;
-	desc->urb.transfer_flags = USB_ASYNC_UNLINK;
 	
-/*	desc->urb.transfer_flags = USB_DISABLE_SPD;*/
-
 	res=usb_submit_urb(&desc->urb);
-	
+
 	if(res){
 		MTS_ERROR("error %d submitting URB\n",(int)res);
 		srb->result = DID_ERROR << 16;
 
 		if(callback)
 			callback(srb);
-	up(&desc->lock); /* no further cleanup is done */
+	        up(&desc->lock);
 
-		goto out;
-	}	
-	
-	MTS_DEBUG_GOT_HERE();
+	}
 
- out:
+out:
 	return err;
 }
 /*
@@ -755,23 +740,21 @@
 
 
 static Scsi_Host_Template mts_scsi_host_template = {
-	name:           "microtek",
+	name:           "microtekX6",
 	detect:		mts_scsi_detect,
 	release:	mts_scsi_release,
-	command:	0,
 	queuecommand:	mts_scsi_queuecommand,
 
 	eh_abort_handler:	mts_scsi_abort,
-	eh_device_reset_handler:0,
-	eh_bus_reset_handler:	0,
 	eh_host_reset_handler:	mts_scsi_host_reset,
 
+	sg_tablesize:		SG_ALL,
 	can_queue:		1,
 	this_id:		-1,
 	cmd_per_lun:		1,
 	present:		0,
 	unchecked_isa_dma:	FALSE,
-	use_clustering:		FALSE,
+	use_clustering:		TRUE,
 	use_new_eh_code:	TRUE,
 	emulated:		TRUE
 };
@@ -782,7 +765,7 @@
 static void mts_usb_disconnect (struct usb_device *dev, void *ptr)
 {
 	struct mts_desc* to_remove = (struct mts_desc*)ptr;
-	
+
 	MTS_DEBUG_GOT_HERE();
 
 	/* leave the list - lock it */
@@ -847,7 +830,7 @@
 	int ep_in_set[3]; /* this will break if we have more than three endpoints
 			   which is why we check */
 	int *ep_in_current = ep_in_set;
-	
+
 	struct mts_desc * new_desc;
 	struct vendor_product const* p;
 
@@ -856,7 +839,7 @@
 
 	MTS_DEBUG_GOT_HERE();
 	MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
-	
+
 	MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
 		   (int)dev->descriptor.idProduct,
 		   (int)dev->descriptor.idVendor );
@@ -871,12 +854,12 @@
 	if ( p->support_status != mts_sup_full )
 		MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n",
 			     p->name );
-	
+
 	/* the altsettting 0 on the interface we're probing */
 	altsetting =
-		&(dev->actconfig->interface[interface].altsetting[0]);	
+		&(dev->actconfig->interface[interface].altsetting[0]);
+
 
-	
 	/* Check if the config is sane */
 
 	if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) {
@@ -886,13 +869,13 @@
 	}
 
 	for( i = 0; i < altsetting->bNumEndpoints; i++ ) {
-		if ((altsetting->endpoint[i].bmAttributes & 
+		if ((altsetting->endpoint[i].bmAttributes &
 		     USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
-			
+
 			MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
 			     (int)altsetting->endpoint[i].bEndpointAddress );
 		} else {
-			if (altsetting->endpoint[i].bEndpointAddress & 
+			if (altsetting->endpoint[i].bEndpointAddress &
 			    USB_DIR_IN)
 				*ep_in_current++
 					= altsetting->endpoint[i].bEndpointAddress &
@@ -902,26 +885,26 @@
 					MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
 					return NULL;
 				}
-		
+
 				ep_out = altsetting->endpoint[i].bEndpointAddress &
 					USB_ENDPOINT_NUMBER_MASK;
 			}
 		}
-		
+
 	}
-	
+
 
 	if ( ep_out == -1 ) {
 		MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
 		return NULL;
 	}
-	
-	
+
+
 	/* I don't understand the following fully (it's from usb-storage) -- John */
 
 	/* set the interface -- STALL is an acceptable response here */
 	result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
-	
+
 	MTS_DEBUG("usb_set_interface returned %d.\n",result);
 	switch( result )
 	{
@@ -970,7 +953,7 @@
 	new_desc->ep_out = ep_out;
 	new_desc->ep_response = ep_in_set[0];
 	new_desc->ep_image = ep_in_set[1];
-	
+
 
 	if ( new_desc->ep_out != MTS_EP_OUT )
 		MTS_WARNING( "will this work? Command EP is not usually %d\n",
@@ -984,16 +967,16 @@
 		MTS_WARNING( "will this work? Image data EP is not usually %d\n",
 			     (int)new_desc->ep_image );
 
-	
+
 	/* Initialize the host template based on the default one */
-	memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template)); 	
+	memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template));
 	/* HACK from usb-storage - this is needed for scsi detection */
 	(struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */
-	
+
 	MTS_DEBUG("registering SCSI module\n");
-	
+
 	new_desc->ctempl.module = THIS_MODULE;
-	result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl));	
+	result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl));
 	/* Will get hit back in microtek_detect by this func */
 	if ( result )
 	{
@@ -1011,12 +994,12 @@
 	down(&mts_list_semaphore);
 
 	mts_add_nolock( new_desc );
-	
+
 	up(&mts_list_semaphore);
-	
-	
+
+
 	MTS_DEBUG("completed probe and exiting happily\n");
-	
+
 	return (void *)new_desc;
 }
 
@@ -1027,7 +1010,7 @@
 int __init microtek_drv_init(void)
 {
 	int result;
-	
+
 	MTS_DEBUG_GOT_HERE();
 	init_MUTEX(&mts_list_semaphore);
 
@@ -1037,14 +1020,14 @@
 	} else {
 		MTS_DEBUG("driver registered.\n");
 	}
-	
+
 	return 0;
 }
 
 void __exit microtek_drv_exit(void)
 {
 	struct mts_desc* next;
-	
+
 	MTS_DEBUG_GOT_HERE();
 
 	usb_deregister(&mts_usb_driver);
@@ -1060,7 +1043,7 @@
 		/* advance the list pointer */
 		mts_list = next;
 	}
-	
+
 	up(&mts_list_semaphore);
 }
 
diff -urN linux-2.4.3-pre6/drivers/usb/microtek.h linux/drivers/usb/microtek.h
--- linux-2.4.3-pre6/drivers/usb/microtek.h	Wed Jun 28 19:49:00 2000
+++ linux/drivers/usb/microtek.h	Mon Dec  3 22:33:05 2001
@@ -21,20 +21,8 @@
 	void* data;
 	unsigned data_length;
 	int data_pipe;
+	int fragment;
 
-	enum {
-		mts_con_none,
-		mts_con_command,
-		mts_con_data,
-		mts_con_status,
-		mts_con_error,
-		mts_con_done
-	}
-	state;
-
-	atomic_t do_abort; /* when != 0 URB completion routines will
-			      return straightaway */
-	
 	u8 status; /* status returned from ep_response after command completion */
 };
 
@@ -44,18 +32,18 @@
 	struct mts_desc *prev;
 
 	struct usb_device *usb_dev;
-	
+
 	int interface;
 
 	/* Endpoint addresses */
 	u8 ep_out;
 	u8 ep_response;
 	u8 ep_image;
-	
+
 	struct Scsi_Host * host;
 	Scsi_Host_Template ctempl;
 	int host_number;
-	
+
 	struct semaphore lock;
 
 	struct urb urb;
@@ -68,5 +56,5 @@
 #define MTS_EP_IMAGE	0x3
 #define MTS_EP_TOTAL	0x3
 
-#define MTS_SCSI_ERR_MASK ~0x3fu 
+#define MTS_SCSI_ERR_MASK ~0x3fu
 
diff -urN linux-2.4.3-pre6/drivers/usb/serial/belkin_sa.c linux/drivers/usb/serial/belkin_sa.c
--- linux-2.4.3-pre6/drivers/usb/serial/belkin_sa.c	Mon Dec  3 22:32:56 2001
+++ linux/drivers/usb/serial/belkin_sa.c	Mon Dec  3 22:33:05 2001
@@ -264,8 +264,8 @@
 			belkin_sa_close (&serial->port[i], NULL);
 		}
 		/* My special items, the standard routines free my urbs */
-		if (serial->port->private)
-			kfree(serial->port->private);
+		if (serial->port[i].private)
+			kfree(serial->port[i].private);
 	}
 }
 
diff -urN linux-2.4.3-pre6/drivers/usb/serial/mct_u232.c linux/drivers/usb/serial/mct_u232.c
--- linux-2.4.3-pre6/drivers/usb/serial/mct_u232.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/serial/mct_u232.c	Mon Dec  3 22:33:05 2001
@@ -346,8 +346,8 @@
 			mct_u232_close (&serial->port[i], NULL);
 		}
 		/* My special items, the standard routines free my urbs */
-		if (serial->port->private)
-			kfree(serial->port->private);
+		if (serial->port[i].private)
+			kfree(serial->port[i].private);
 	}
 } /* mct_u232_shutdown */
 
diff -urN linux-2.4.3-pre6/drivers/usb/serial/omninet.c linux/drivers/usb/serial/omninet.c
--- linux-2.4.3-pre6/drivers/usb/serial/omninet.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/serial/omninet.c	Mon Dec  3 22:33:05 2001
@@ -305,8 +305,6 @@
 		return (0);
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count, buf);
-	
 	spin_lock_irqsave (&port->port_lock, flags);
 	
 	count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
@@ -318,6 +316,7 @@
 		memcpy (wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count);
 	}
 
+	usb_serial_debug_data (__FILE__, __FUNCTION__, count, wport->write_urb->transfer_buffer);
 
 	header->oh_seq 	= od->od_outseq++;
 	header->oh_len 	= count;
diff -urN linux-2.4.3-pre6/drivers/usb/serial/usbserial.c linux/drivers/usb/serial/usbserial.c
--- linux-2.4.3-pre6/drivers/usb/serial/usbserial.c	Mon Dec  3 22:32:56 2001
+++ linux/drivers/usb/serial/usbserial.c	Mon Dec  3 22:33:05 2001
@@ -827,14 +827,14 @@
 		spin_lock_irqsave (&port->port_lock, flags);
 		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 
-		usb_serial_debug_data (__FILE__, __FUNCTION__, count, buf);
-
 		if (from_user) {
 			copy_from_user(port->write_urb->transfer_buffer, buf, count);
 		}
 		else {
 			memcpy (port->write_urb->transfer_buffer, buf, count);
 		}  
+
+		usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
 
 		/* set up our urb */
 		FILL_BULK_URB(port->write_urb, serial->dev, 
diff -urN linux-2.4.3-pre6/drivers/usb/serial/whiteheat.c linux/drivers/usb/serial/whiteheat.c
--- linux-2.4.3-pre6/drivers/usb/serial/whiteheat.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/serial/whiteheat.c	Mon Dec  3 22:33:05 2001
@@ -1,7 +1,7 @@
 /*
  * USB ConnectTech WhiteHEAT driver
  *
- *	Copyright (C) 1999, 2000
+ *	Copyright (C) 1999 - 2001
  *	    Greg Kroah-Hartman (greg@kroah.com)
  *
  *	This program is free software; you can redistribute it and/or modify
@@ -11,6 +11,11 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * 2001_Mar_19 gkh
+ *	Fixed MOD_INC and MOD_DEC logic, the ability to open a port more 
+ *	than once, and the got the proper usb_device_id table entries so
+ *	the driver works again.
+ *
  * (11/01/2000) Adam J. Richter
  *	usb_device_id table support
  * 
@@ -93,7 +98,7 @@
 };
 
 static __devinitdata struct usb_device_id id_table_prerenumeration [] = {
-	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
+	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
 	{ }						/* Terminating entry */
 };
 
@@ -289,43 +294,44 @@
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
-	if (port->active) {
-		dbg (__FUNCTION__ " - device already open");
-		return -EINVAL;
-	}
-	port->active = 1;
-
-	/* set up some stuff for our command port */
-	command_port = &port->serial->port[COMMAND_PORT];
-	if (command_port->private == NULL) {
-		info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL);
-		if (info == NULL) {
-			err(__FUNCTION__ " - out of memory");
-			return -ENOMEM;
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+	
+	if (!port->active) {
+		port->active = 1;
+
+		/* set up some stuff for our command port */
+		command_port = &port->serial->port[COMMAND_PORT];
+		if (command_port->private == NULL) {
+			info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL);
+			if (info == NULL) {
+				err(__FUNCTION__ " - out of memory");
+				return -ENOMEM;
+			}
+			
+			init_waitqueue_head(&info->wait_command);
+			command_port->private = info;
+			command_port->write_urb->complete = command_port_write_callback;
+			command_port->read_urb->complete = command_port_read_callback;
+			command_port->read_urb->dev = port->serial->dev;
+			command_port->tty = port->tty;		/* need this to "fake" our our sanity check macros */
+			usb_submit_urb (command_port->read_urb);
 		}
 		
-		init_waitqueue_head(&info->wait_command);
-		command_port->private = info;
-		command_port->write_urb->complete = command_port_write_callback;
-		command_port->read_urb->complete = command_port_read_callback;
-		command_port->read_urb->dev = port->serial->dev;
-		command_port->tty = port->tty;		/* need this to "fake" our our sanity check macros */
-		usb_submit_urb (command_port->read_urb);
-	}
+		/* Start reading from the device */
+		port->read_urb->dev = port->serial->dev;
+		result = usb_submit_urb(port->read_urb);
+		if (result)
+			err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+	
+		/* send an open port command */
+		/* firmware uses 1 based port numbering */
+		open_command.port = port->number - port->serial->minor + 1;
+		whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
 	
-	/* Start reading from the device */
-	port->read_urb->dev = port->serial->dev;
-	result = usb_submit_urb(port->read_urb);
-	if (result)
-		err(__FUNCTION__ " - failed submitting read urb, error %d", result);
-
-	/* send an open port command */
-	/* firmware uses 1 based port numbering */
-	open_command.port = port->number - port->serial->minor + 1;
-	whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
-
-	/* Need to do device specific setup here (control lines, baud rate, etc.) */
-	/* FIXME!!! */
+		/* Need to do device specific setup here (control lines, baud rate, etc.) */
+		/* FIXME!!! */
+	}
 
 	dbg(__FUNCTION__ " - exit");
 	
@@ -339,18 +345,23 @@
 	
 	dbg(__FUNCTION__ " - port %d", port->number);
 	
-	/* send a close command to the port */
-	/* firmware uses 1 based port numbering */
-	close_command.port = port->number - port->serial->minor + 1;
-	whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command));
+	--port->open_count;
 
-	/* Need to change the control lines here */
-	/* FIXME */
+	if (port->open_count <= 0) {
+		/* send a close command to the port */
+		/* firmware uses 1 based port numbering */
+		close_command.port = port->number - port->serial->minor + 1;
+		whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command));
 	
-	/* shutdown our bulk reads and writes */
-	usb_unlink_urb (port->write_urb);
-	usb_unlink_urb (port->read_urb);
-	port->active = 0;
+		/* Need to change the control lines here */
+		/* FIXME */
+		
+		/* shutdown our bulk reads and writes */
+		usb_unlink_urb (port->write_urb);
+		usb_unlink_urb (port->read_urb);
+		port->active = 0;
+	}
+	MOD_DEC_USE_COUNT;
 }
 
 
@@ -548,8 +559,16 @@
 static void whiteheat_shutdown (struct usb_serial *serial)
 {
 	struct usb_serial_port 		*command_port;
+	int i;
 
 	dbg(__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		while (serial->port[i].open_count > 0) {
+			whiteheat_close (&serial->port[i], NULL);
+		}
+	}
 
 	/* free up our private data for our command port */
 	command_port = &serial->port[COMMAND_PORT];
diff -urN linux-2.4.3-pre6/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- linux-2.4.3-pre6/drivers/usb/uhci.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/uhci.c	Mon Dec  3 22:33:05 2001
@@ -66,6 +66,10 @@
 static int uhci_unlink_generic(struct urb *urb);
 static int uhci_unlink_urb(struct urb *urb);
 
+static int  ports_active(struct uhci *uhci);
+static void suspend_hc(struct uhci *uhci);
+static void wakeup_hc(struct uhci *uhci);
+
 #define min(a,b) (((a)<(b))?(a):(b))
 
 /* If a transfer is still active after this much time, turn off FSBR */
@@ -1767,6 +1771,10 @@
 	}
 	nested_unlock(&uhci->urblist_lock, flags);
 
+	/* enter global suspend if nothing connected */
+	if (!uhci->is_suspended && !ports_active(uhci))
+		suspend_hc(uhci);
+
 	rh_init_int_timer(urb);
 }
 
@@ -2037,19 +2045,21 @@
 		return;
 	outw(status, io_addr + USBSTS);
 
-	if (status & ~(USBSTS_USBINT | USBSTS_ERROR)) {
-		if (status & USBSTS_RD)
-			printk(KERN_INFO "uhci: resume detected, not implemented\n");
+	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
 		if (status & USBSTS_HSE)
 			printk(KERN_ERR "uhci: host system error, PCI problems?\n");
 		if (status & USBSTS_HCPE)
 			printk(KERN_ERR "uhci: host controller process error. something bad happened\n");
-		if (status & USBSTS_HCH) {
+		if ((status & USBSTS_HCH) && !uhci->is_suspended) {
 			printk(KERN_ERR "uhci: host controller halted. very bad\n");
 			/* FIXME: Reset the controller, fix the offending TD */
 		}
 	}
 
+	if (status & USBSTS_RD) {
+		wakeup_hc(uhci);
+	}
+
 	uhci_free_pending_qhs(uhci);
 
 	spin_lock(&uhci->urb_remove_lock);
@@ -2093,6 +2103,49 @@
 	wait_ms(50);
 	outw(0, io_addr + USBCMD);
 	wait_ms(10);
+}
+
+static void suspend_hc(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+
+	dbg("suspend_hc");
+
+	outw(USBCMD_EGSM, io_addr + USBCMD);
+
+	uhci->is_suspended = 1;
+}
+
+static void wakeup_hc(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	unsigned int status;
+
+	dbg("wakeup_hc");
+
+	outw(0, io_addr + USBCMD);
+	
+	/* wait for EOP to be sent */
+	status = inw(io_addr + USBCMD);
+	while (status & USBCMD_FGR)
+		status = inw(io_addr + USBCMD);
+
+	uhci->is_suspended = 0;
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+}
+
+static int ports_active(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	int connection = 0;
+	int i;
+
+	for (i = 0; i < uhci->rh.numports; i++)
+		connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1);
+
+	return connection;
 }
 
 static void start_hc(struct uhci *uhci)
diff -urN linux-2.4.3-pre6/drivers/usb/uhci.h linux/drivers/usb/uhci.h
--- linux-2.4.3-pre6/drivers/usb/uhci.h	Wed Feb 21 16:11:49 2001
+++ linux/drivers/usb/uhci.h	Mon Dec  3 22:33:05 2001
@@ -321,6 +321,7 @@
 	spinlock_t framelist_lock;
 	struct uhci_framelist *fl;		/* Frame list */
 	int fsbr;				/* Full speed bandwidth reclamation */
+	int is_suspended;
 
 	spinlock_t qh_remove_lock;
 	struct list_head qh_remove_list;
diff -urN linux-2.4.3-pre6/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
--- linux-2.4.3-pre6/drivers/usb/usb-ohci.c	Mon Dec  3 22:32:56 2001
+++ linux/drivers/usb/usb-ohci.c	Mon Dec  3 22:33:05 2001
@@ -2331,6 +2331,13 @@
 	unsigned long mem_resource, mem_len;
 	void *mem_base;
 
+	/* blacklisted hardware? */
+	if (id->driver_data) {
+		info ("%s (%s): %s", dev->slot_name,
+			dev->name, (char *) id->driver_data);
+		return -ENODEV;
+	}
+
 	if (pci_enable_device(dev) < 0)
 		return -ENODEV;
 	
@@ -2495,6 +2502,20 @@
 /*-------------------------------------------------------------------------*/
 
 static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
+
+	/*
+	 * AMD-756 [Viper] USB has a serious erratum when used with
+	 * lowspeed devices like mice; oopses have been seen.  The
+	 * vendor workaround needs an NDA ... for now, blacklist it.
+	 */
+	vendor:		0x1022,
+	device:		0x740c,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+
+	driver_data:	(unsigned long) "blacklisted, erratum #4",
+
+} , {
 
 	/* handle any USB OHCI controller */
 	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x10),
diff -urN linux-2.4.3-pre6/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux-2.4.3-pre6/drivers/usb/usb.c	Mon Dec  3 22:32:56 2001
+++ linux/drivers/usb/usb.c	Mon Dec  3 22:33:05 2001
@@ -39,11 +39,6 @@
 #endif
 #include <linux/usb.h>
 
-#define DEVNUM_ROUND_ROBIN	/***** OPTION *****/
-#ifdef DEVNUM_ROUND_ROBIN
-static int devnum_next = 1;
-#endif
-
 static const int usb_bandwidth_option =
 #ifdef CONFIG_USB_BANDWIDTH
 				1;
@@ -367,6 +362,10 @@
 
 	memset(&bus->devmap, 0, sizeof(struct usb_devmap));
 
+#ifdef DEVNUM_ROUND_ROBIN
+	bus->devnum_next = 1;
+#endif /* DEVNUM_ROUND_ROBIN */
+
 	bus->op = op;
 	bus->root_hub = NULL;
 	bus->hcpriv = NULL;
@@ -1707,14 +1706,12 @@
 #ifndef DEVNUM_ROUND_ROBIN
 	devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
 #else	/* round_robin alloc of devnums */
-	/* Try to allocate the next devnum beginning at devnum_next. */
-	devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, devnum_next);
+	/* Try to allocate the next devnum beginning at bus->devnum_next. */
+	devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next);
 	if (devnum >= 128)
 		devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
 
-	devnum_next = devnum + 1;
-	if (devnum_next >= 128)
-		devnum_next = 1;
+	dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
 #endif	/* round_robin alloc of devnums */
 
 	if (devnum < 128) {
diff -urN linux-2.4.3-pre6/fs/buffer.c linux/fs/buffer.c
--- linux-2.4.3-pre6/fs/buffer.c	Mon Dec  3 22:32:56 2001
+++ linux/fs/buffer.c	Mon Dec  3 22:33:05 2001
@@ -1880,8 +1880,8 @@
 	iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
 	
 	page = grab_cache_page(mapping, index);
-	err = PTR_ERR(page);
-	if (IS_ERR(page))
+	err = -ENOMEM;
+	if (!page)
 		goto out;
 
 	if (!page->buffers)
diff -urN linux-2.4.3-pre6/fs/exec.c linux/fs/exec.c
--- linux-2.4.3-pre6/fs/exec.c	Mon Dec  3 22:32:56 2001
+++ linux/fs/exec.c	Mon Dec  3 22:33:05 2001
@@ -252,6 +252,8 @@
 /*
  * This routine is used to map in a page into an address space: needed by
  * execve() for the initial stack and environment pages.
+ *
+ * tsk->mmap_sem is held for writing.
  */
 void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address)
 {
@@ -275,6 +277,7 @@
 	flush_dcache_page(page);
 	flush_page_to_ram(page);
 	set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, PAGE_COPY))));
+	tsk->mm->rss++;
 	spin_unlock(&tsk->mm->page_table_lock);
 
 	/* no need for flush_tlb */
@@ -322,7 +325,6 @@
 		struct page *page = bprm->page[i];
 		if (page) {
 			bprm->page[i] = NULL;
-			current->mm->rss++;
 			put_dirty_page(current,page,stack_base);
 		}
 		stack_base += PAGE_SIZE;
diff -urN linux-2.4.3-pre6/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- linux-2.4.3-pre6/fs/ext2/ialloc.c	Fri Dec  8 17:35:54 2000
+++ linux/fs/ext2/ialloc.c	Mon Dec  3 22:33:05 2001
@@ -442,6 +442,7 @@
 	inode->u.ext2_i.i_file_acl = 0;
 	inode->u.ext2_i.i_dir_acl = 0;
 	inode->u.ext2_i.i_dtime = 0;
+	inode->u.ext2_i.i_prealloc_count = 0;
 	inode->u.ext2_i.i_block_group = i;
 	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
 		inode->i_flags |= S_SYNC;
diff -urN linux-2.4.3-pre6/fs/ext2/inode.c linux/fs/ext2/inode.c
--- linux-2.4.3-pre6/fs/ext2/inode.c	Fri Dec 29 14:36:44 2000
+++ linux/fs/ext2/inode.c	Mon Dec  3 22:33:05 2001
@@ -1047,6 +1047,7 @@
 		inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
 	}
 	inode->i_generation = le32_to_cpu(raw_inode->i_generation);
+	inode->u.ext2_i.i_prealloc_count = 0;
 	inode->u.ext2_i.i_block_group = block_group;
 
 	/*
diff -urN linux-2.4.3-pre6/fs/inode.c linux/fs/inode.c
--- linux-2.4.3-pre6/fs/inode.c	Mon Dec  3 22:32:56 2001
+++ linux/fs/inode.c	Mon Dec  3 22:33:05 2001
@@ -133,7 +133,7 @@
 
 	if (sb) {
 		/* Don't do this for I_DIRTY_PAGES - that doesn't actually dirty the inode itself */
-		if (flags & (I_DIRTY | I_DIRTY_SYNC)) {
+		if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
 			if (sb->s_op && sb->s_op->dirty_inode)
 				sb->s_op->dirty_inode(inode);
 		}
diff -urN linux-2.4.3-pre6/fs/namei.c linux/fs/namei.c
--- linux-2.4.3-pre6/fs/namei.c	Mon Dec  3 22:32:56 2001
+++ linux/fs/namei.c	Mon Dec  3 22:33:05 2001
@@ -1927,13 +1927,11 @@
 	 * bloody create() on broken symlinks. Furrfu...
 	 */
 	name = __getname();
-	if (IS_ERR(name))
-		goto fail_name;
+	if (!name)
+		return -ENOMEM;
 	strcpy(name, nd->last.name);
 	nd->last.name = name;
 	return 0;
-fail_name:
-	link = name;
 fail:
 	path_release(nd);
 	return PTR_ERR(link);
diff -urN linux-2.4.3-pre6/fs/proc/proc_misc.c linux/fs/proc/proc_misc.c
--- linux-2.4.3-pre6/fs/proc/proc_misc.c	Tue Nov  7 11:08:09 2000
+++ linux/fs/proc/proc_misc.c	Mon Dec  3 22:33:05 2001
@@ -293,8 +293,8 @@
 		"page %u %u\n"
                 "swap %u %u\n"
 		"intr %u",
-			kstat.pgpgin,
-			kstat.pgpgout,
+			kstat.pgpgin >> 1,
+			kstat.pgpgout >> 1,
 			kstat.pswpin,
 			kstat.pswpout,
 			sum
diff -urN linux-2.4.3-pre6/fs/reiserfs/inode.c linux/fs/reiserfs/inode.c
--- linux-2.4.3-pre6/fs/reiserfs/inode.c	Mon Dec  3 22:32:56 2001
+++ linux/fs/reiserfs/inode.c	Mon Dec  3 22:33:05 2001
@@ -476,8 +476,8 @@
     index = tail_offset >> PAGE_CACHE_SHIFT ;
     if (index != hole_page->index) {
 	tail_page = grab_cache_page(inode->i_mapping, index) ;
-	retval = PTR_ERR(tail_page) ;
-	if (IS_ERR(tail_page)) {
+	retval = -ENOMEM;
+	if (!tail_page) {
 	    goto out ;
 	}
     } else {
@@ -1487,8 +1487,8 @@
         return -ENOENT ;
     }
     page = grab_cache_page(p_s_inode->i_mapping, index) ;
-    error = PTR_ERR(page) ;
-    if (IS_ERR(page)) {
+    error = -ENOMEM ;
+    if (!page) {
         goto out ;
     }
     /* start within the page of the last block in the file */
diff -urN linux-2.4.3-pre6/fs/reiserfs/ioctl.c linux/fs/reiserfs/ioctl.c
--- linux-2.4.3-pre6/fs/reiserfs/ioctl.c	Mon Jan 15 12:42:32 2001
+++ linux/fs/reiserfs/ioctl.c	Mon Dec  3 22:33:05 2001
@@ -77,8 +77,8 @@
     */
     index = inode->i_size >> PAGE_CACHE_SHIFT ;
     page = grab_cache_page(inode->i_mapping, index) ;
-    retval = PTR_ERR(page) ;
-    if (IS_ERR(page)) {
+    retval = -ENOMEM;
+    if (!page) {
         goto out ;
     }
     retval = reiserfs_prepare_write(NULL, page, write_from, blocksize) ;
diff -urN linux-2.4.3-pre6/fs/smbfs/cache.c linux/fs/smbfs/cache.c
--- linux-2.4.3-pre6/fs/smbfs/cache.c	Mon Dec  3 22:32:56 2001
+++ linux/fs/smbfs/cache.c	Mon Dec  3 22:33:05 2001
@@ -48,6 +48,7 @@
 	UnlockPage(page);
 	page_cache_release(page);
 out:
+	return;
 }
 
 /*
diff -urN linux-2.4.3-pre6/include/asm-i386/string.h linux/include/asm-i386/string.h
--- linux-2.4.3-pre6/include/asm-i386/string.h	Mon Dec  3 22:32:56 2001
+++ linux/include/asm-i386/string.h	Mon Dec  3 22:33:06 2001
@@ -542,10 +542,10 @@
 {
 	if (!size)
 		return addr;
-	__asm__("repnz; scasb
-		jnz 1f
-		dec %%edi
-1:		"
+	__asm__("repnz; scasb\n\t"
+		"jnz 1f\n\t"
+		"dec %%edi\n"
+		"1:"
 		: "=D" (addr), "=c" (size)
 		: "0" (addr), "1" (size), "a" (c));
 	return addr;
diff -urN linux-2.4.3-pre6/include/asm-i386/system.h linux/include/asm-i386/system.h
--- linux-2.4.3-pre6/include/asm-i386/system.h	Wed Feb 21 16:09:57 2001
+++ linux/include/asm-i386/system.h	Mon Dec  3 22:33:06 2001
@@ -144,16 +144,17 @@
 extern inline void __set_64bit (unsigned long long * ptr,
 		unsigned int low, unsigned int high)
 {
-__asm__ __volatile__ (
-	"1:	movl (%0), %%eax;
-		movl 4(%0), %%edx;
-		cmpxchg8b (%0);
-		jnz 1b"
-	::		"D"(ptr),
+	__asm__ __volatile__ (
+		"\n1:\t"
+		"movl (%0), %%eax\n\t"
+		"movl 4(%0), %%edx\n\t"
+		"cmpxchg8b (%0)\n\t"
+		"jnz 1b"
+		: /* no outputs */
+		:	"D"(ptr),
 			"b"(low),
 			"c"(high)
-	:
-			"ax","dx","memory");
+		:	"ax","dx","memory");
 }
 
 extern void inline __set_64bit_constant (unsigned long long *ptr,
diff -urN linux-2.4.3-pre6/include/linux/cdrom.h linux/include/linux/cdrom.h
--- linux-2.4.3-pre6/include/linux/cdrom.h	Wed Feb 21 16:09:58 2001
+++ linux/include/linux/cdrom.h	Mon Dec  3 22:33:06 2001
@@ -524,10 +524,12 @@
 	__u32 end_sector_l0;
 };
 
+#define DVD_LAYERS	4
+
 struct dvd_physical {
 	__u8 type;
 	__u8 layer_num;
-	struct dvd_layer layer[4];
+	struct dvd_layer layer[DVD_LAYERS];
 };
 
 struct dvd_copyright {
diff -urN linux-2.4.3-pre6/include/linux/etherdevice.h linux/include/linux/etherdevice.h
--- linux-2.4.3-pre6/include/linux/etherdevice.h	Fri Oct 27 12:22:34 2000
+++ linux/include/linux/etherdevice.h	Mon Dec  3 22:33:06 2001
@@ -45,6 +45,14 @@
 	memcpy (dest->data, src, len);
 }
 
+/* Check that the ethernet address (MAC) is not 00:00:00:00:00:00 and is not
+ * a multicast address.  Return true if the address is valid.
+ */
+static __inline__ int is_valid_ether_addr( u8 *addr )
+{
+	return !(addr[0]&1) && memcmp( addr, "\0\0\0\0\0\0", 6);
+}
+
 #endif
 
 #endif	/* _LINUX_ETHERDEVICE_H */
diff -urN linux-2.4.3-pre6/include/linux/sched.h linux/include/linux/sched.h
--- linux-2.4.3-pre6/include/linux/sched.h	Mon Dec  3 22:32:56 2001
+++ linux/include/linux/sched.h	Mon Dec  3 22:33:06 2001
@@ -167,7 +167,7 @@
  */
 struct files_struct {
 	atomic_t count;
-	rwlock_t file_lock;
+	rwlock_t file_lock;	/* Protects all the below members.  Nests inside tsk->alloc_lock */
 	int max_fds;
 	int max_fdset;
 	int next_fd;
@@ -209,9 +209,12 @@
 	atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) */
 	int map_count;				/* number of VMAs */
 	struct rw_semaphore mmap_sem;
-	spinlock_t page_table_lock;
+	spinlock_t page_table_lock;		/* Protects task page tables and mm->rss */
 
-	struct list_head mmlist;		/* List of all active mm's */
+	struct list_head mmlist;		/* List of all active mm's.  These are globally strung
+						 * together off init_mm.mmlist, and are protected
+						 * by mmlist_lock
+						 */
 
 	unsigned long start_code, end_code, start_data, end_data;
 	unsigned long start_brk, brk, start_stack;
@@ -857,6 +860,7 @@
 	write_unlock_irq(&tasklist_lock);
 }
 
+/* Protects ->fs, ->files, ->mm, and synchronises with wait4().  Nests inside tasklist_lock */
 static inline void task_lock(struct task_struct *p)
 {
 	spin_lock(&p->alloc_lock);
diff -urN linux-2.4.3-pre6/include/linux/tqueue.h linux/include/linux/tqueue.h
--- linux-2.4.3-pre6/include/linux/tqueue.h	Wed Feb 21 16:09:57 2001
+++ linux/include/linux/tqueue.h	Mon Dec  3 22:33:06 2001
@@ -42,6 +42,25 @@
 	void *data;			/* argument to function */
 };
 
+/*
+ * Emit code to initialise a tq_struct's routine and data pointers
+ */
+#define PREPARE_TQUEUE(_tq, _routine, _data)			\
+	do {							\
+		(_tq)->routine = _routine;			\
+		(_tq)->data = _data;				\
+	} while (0)
+
+/*
+ * Emit code to initialise all of a tq_struct
+ */
+#define INIT_TQUEUE(_tq, _routine, _data)			\
+	do {							\
+		INIT_LIST_HEAD(&(_tq)->list);			\
+		(_tq)->sync = 0;				\
+		PREPARE_TQUEUE((_tq), (_routine), (_data));	\
+	} while (0)
+
 typedef struct list_head task_queue;
 
 #define DECLARE_TASK_QUEUE(q)	LIST_HEAD(q)
diff -urN linux-2.4.3-pre6/include/linux/tty.h linux/include/linux/tty.h
--- linux-2.4.3-pre6/include/linux/tty.h	Wed Feb 21 16:09:58 2001
+++ linux/include/linux/tty.h	Mon Dec  3 22:33:06 2001
@@ -307,6 +307,8 @@
 	struct semaphore atomic_read;
 	struct semaphore atomic_write;
 	spinlock_t read_lock;
+	/* If the tty has a pending do_SAK, queue it here - akpm */
+	struct tq_struct SAK_tq;
 };
 
 /* tty magic number */
diff -urN linux-2.4.3-pre6/include/linux/usb.h linux/include/linux/usb.h
--- linux-2.4.3-pre6/include/linux/usb.h	Wed Feb 21 16:11:24 2001
+++ linux/include/linux/usb.h	Mon Dec  3 22:33:06 2001
@@ -555,11 +555,17 @@
 	int (*unlink_urb) (struct urb* purb);
 };
 
+#define DEVNUM_ROUND_ROBIN	/***** OPTION *****/
+
 /*
  * Allocated per bus we have
  */
 struct usb_bus {
 	int busnum;			/* Bus number (in order of reg) */
+
+#ifdef DEVNUM_ROUND_ROBIN
+	int devnum_next;                /* Next open device number in round-robin allocation */
+#endif /* DEVNUM_ROUND_ROBIN */
 
 	struct usb_devmap devmap;       /* Device map */
 	struct usb_operations *op;      /* Operations (specific to the HC) */
diff -urN linux-2.4.3-pre6/kernel/sched.c linux/kernel/sched.c
--- linux-2.4.3-pre6/kernel/sched.c	Mon Dec  3 22:32:56 2001
+++ linux/kernel/sched.c	Mon Dec  3 22:33:06 2001
@@ -82,6 +82,8 @@
  *
  * If both locks are to be concurrently held, the runqueue_lock
  * nests inside the tasklist_lock.
+ *
+ * task->alloc_lock nests inside tasklist_lock.
  */
 spinlock_t runqueue_lock __cacheline_aligned = SPIN_LOCK_UNLOCKED;  /* inner */
 rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED;	/* outer */
diff -urN linux-2.4.3-pre6/mm/filemap.c linux/mm/filemap.c
--- linux-2.4.3-pre6/mm/filemap.c	Mon Dec  3 22:32:56 2001
+++ linux/mm/filemap.c	Mon Dec  3 22:33:06 2001
@@ -659,7 +659,7 @@
 
 /*
  * a rather lightweight function, finding and getting a reference to a
- * hashed page atomically, waiting for it if it's locked.
+ * hashed page atomically.
  */
 struct page * __find_get_page(struct address_space *mapping,
 			      unsigned long offset, struct page **hash)
@@ -679,7 +679,8 @@
 }
 
 /*
- * Get the lock to a page atomically.
+ * Same as the above, but lock the page too, verifying that
+ * it's still valid once we own it.
  */
 struct page * __find_lock_page (struct address_space *mapping,
 				unsigned long offset, struct page **hash)
diff -urN linux-2.4.3-pre6/mm/memory.c linux/mm/memory.c
--- linux-2.4.3-pre6/mm/memory.c	Mon Dec  3 22:32:56 2001
+++ linux/mm/memory.c	Mon Dec  3 22:33:06 2001
@@ -374,7 +374,6 @@
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	} while (address && (address < end));
-	spin_unlock(&mm->page_table_lock);
 	/*
 	 * Update rss for the mm_struct (not necessarily current->mm)
 	 * Notice that rss is an unsigned long.
@@ -383,6 +382,7 @@
 		mm->rss -= freed;
 	else
 		mm->rss = 0;
+	spin_unlock(&mm->page_table_lock);
 }
 
 
@@ -792,6 +792,8 @@
  *  - flush the old one
  *  - update the page tables
  *  - inform the TLB about the new one
+ *
+ * We hold the mm semaphore for reading and vma->vm_mm->page_table_lock
  */
 static inline void establish_pte(struct vm_area_struct * vma, unsigned long address, pte_t *page_table, pte_t entry)
 {
@@ -800,6 +802,9 @@
 	update_mmu_cache(vma, address, entry);
 }
 
+/*
+ * We hold the mm semaphore for reading and vma->vm_mm->page_table_lock
+ */
 static inline void break_cow(struct vm_area_struct * vma, struct page *	old_page, struct page * new_page, unsigned long address, 
 		pte_t *page_table)
 {
@@ -1024,25 +1029,26 @@
 }
 
 /*
- * We hold the mm semaphore and the page_table_lock on entry
- * and exit.
+ * We hold the mm semaphore and the page_table_lock on entry and exit.
  */
 static int do_swap_page(struct mm_struct * mm,
 	struct vm_area_struct * vma, unsigned long address,
 	pte_t * page_table, swp_entry_t entry, int write_access)
 {
-	struct page *page = lookup_swap_cache(entry);
+	struct page *page;
 	pte_t pte;
 
+	spin_unlock(&mm->page_table_lock);
+	page = lookup_swap_cache(entry);
 	if (!page) {
-		spin_unlock(&mm->page_table_lock);
 		lock_kernel();
 		swapin_readahead(entry);
 		page = read_swap_cache(entry);
 		unlock_kernel();
-		spin_lock(&mm->page_table_lock);
-		if (!page)
+		if (!page) {
+			spin_lock(&mm->page_table_lock);
 			return -1;
+		}
 
 		flush_page_to_ram(page);
 		flush_icache_page(vma, page);
@@ -1053,13 +1059,13 @@
 	 * Must lock page before transferring our swap count to already
 	 * obtained page count.
 	 */
-	spin_unlock(&mm->page_table_lock);
 	lock_page(page);
-	spin_lock(&mm->page_table_lock);
 
 	/*
-	 * Back out if somebody else faulted in this pte while we slept.
+	 * Back out if somebody else faulted in this pte while we
+	 * released the page table lock.
 	 */
+	spin_lock(&mm->page_table_lock);
 	if (pte_present(*page_table)) {
 		UnlockPage(page);
 		page_cache_release(page);
diff -urN linux-2.4.3-pre6/mm/mmap.c linux/mm/mmap.c
--- linux-2.4.3-pre6/mm/mmap.c	Mon Dec  3 22:32:56 2001
+++ linux/mm/mmap.c	Mon Dec  3 22:33:06 2001
@@ -177,7 +177,7 @@
  * internally. Essentially, translate the "PROT_xxx" and "MAP_xxx" bits
  * into "VM_xxx".
  */
-static inline unsigned long vm_flags(unsigned long prot, unsigned long flags)
+static inline unsigned long calc_vm_flags(unsigned long prot, unsigned long flags)
 {
 #define _trans(x,bit1,bit2) \
 ((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0)
@@ -200,12 +200,10 @@
 {
 	struct mm_struct * mm = current->mm;
 	struct vm_area_struct * vma;
+	unsigned int vm_flags;
 	int correct_wcount = 0;
 	int error;
 
-	if (file && (!file->f_op || !file->f_op->mmap))
-		return -ENODEV;
-
 	if ((len = PAGE_ALIGN(len)) == 0)
 		return addr;
 
@@ -220,19 +218,36 @@
 	if (mm->map_count > MAX_MAP_COUNT)
 		return -ENOMEM;
 
+	/* Obtain the address to map to. we verify (or select) it and ensure
+	 * that it represents a valid section of the address space.
+	 */
+	if (flags & MAP_FIXED) {
+		if (addr & ~PAGE_MASK)
+			return -EINVAL;
+	} else {
+		addr = get_unmapped_area(addr, len);
+		if (!addr)
+			return -ENOMEM;
+	}
+
+	/* Do simple checking here so the lower-level routines won't have
+	 * to. we assume access permissions have been handled by the open
+	 * of the memory object, so we don't do any here.
+	 */
+	vm_flags = calc_vm_flags(prot,flags) | mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
+
 	/* mlock MCL_FUTURE? */
-	if (mm->def_flags & VM_LOCKED) {
+	if (vm_flags & VM_LOCKED) {
 		unsigned long locked = mm->locked_vm << PAGE_SHIFT;
 		locked += len;
 		if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
 			return -EAGAIN;
 	}
 
-	/* Do simple checking here so the lower-level routines won't have
-	 * to. we assume access permissions have been handled by the open
-	 * of the memory object, so we don't do any here.
-	 */
-	if (file != NULL) {
+	if (file) {
+		if (!file->f_op || !file->f_op->mmap)
+			return -ENODEV;
+
 		switch (flags & MAP_TYPE) {
 		case MAP_SHARED:
 			if ((prot & PROT_WRITE) && !(file->f_mode & FMODE_WRITE))
@@ -246,6 +261,10 @@
 			if (locks_verify_locked(file->f_dentry->d_inode))
 				return -EAGAIN;
 
+			vm_flags |= VM_SHARED | VM_MAYSHARE;
+			if (!(file->f_mode & FMODE_WRITE))
+				vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
+
 			/* fall through */
 		case MAP_PRIVATE:
 			if (!(file->f_mode & FMODE_READ))
@@ -255,18 +274,43 @@
 		default:
 			return -EINVAL;
 		}
+	} else {
+		vm_flags |= VM_SHARED | VM_MAYSHARE;
+		switch (flags & MAP_TYPE) {
+		default:
+			return -EINVAL;
+		case MAP_PRIVATE:
+			vm_flags &= ~(VM_SHARED | VM_MAYSHARE);
+			/* fall through */
+		case MAP_SHARED:
+			break;
+		}
 	}
 
-	/* Obtain the address to map to. we verify (or select) it and ensure
-	 * that it represents a valid section of the address space.
-	 */
-	if (flags & MAP_FIXED) {
-		if (addr & ~PAGE_MASK)
-			return -EINVAL;
-	} else {
-		addr = get_unmapped_area(addr, len);
-		if (!addr)
-			return -ENOMEM;
+	/* Clear old maps */
+	error = -ENOMEM;
+	if (do_munmap(mm, addr, len))
+		return -ENOMEM;
+
+	/* Check against address space limit. */
+	if ((mm->total_vm << PAGE_SHIFT) + len
+	    > current->rlim[RLIMIT_AS].rlim_cur)
+		return -ENOMEM;
+
+	/* Private writable mapping? Check memory availability.. */
+	if ((vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
+	    !(flags & MAP_NORESERVE)				 &&
+	    !vm_enough_memory(len >> PAGE_SHIFT))
+		return -ENOMEM;
+
+	/* Can we just expand an old anonymous mapping? */
+	if (addr && !file && !(vm_flags & VM_SHARED)) {
+		struct vm_area_struct * vma = find_vma(mm, addr-1);
+		if (vma && vma->vm_end == addr && !vma->vm_file && 
+		    vma->vm_flags == vm_flags) {
+			vma->vm_end = addr + len;
+			goto out;
+		}
 	}
 
 	/* Determine the object being mapped and call the appropriate
@@ -280,58 +324,16 @@
 	vma->vm_mm = mm;
 	vma->vm_start = addr;
 	vma->vm_end = addr + len;
-	vma->vm_flags = vm_flags(prot,flags) | mm->def_flags;
-
-	if (file) {
-		VM_ClearReadHint(vma);
-		vma->vm_raend = 0;
-
-		if (file->f_mode & FMODE_READ)
-			vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
-		if (flags & MAP_SHARED) {
-			vma->vm_flags |= VM_SHARED | VM_MAYSHARE;
-
-			/* This looks strange, but when we don't have the file open
-			 * for writing, we can demote the shared mapping to a simpler
-			 * private mapping. That also takes care of a security hole
-			 * with ptrace() writing to a shared mapping without write
-			 * permissions.
-			 *
-			 * We leave the VM_MAYSHARE bit on, just to get correct output
-			 * from /proc/xxx/maps..
-			 */
-			if (!(file->f_mode & FMODE_WRITE))
-				vma->vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
-		}
-	} else {
-		vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
-		if (flags & MAP_SHARED)
-			vma->vm_flags |= VM_SHARED | VM_MAYSHARE;
-	}
-	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_flags = vm_flags;
+	vma->vm_page_prot = protection_map[vm_flags & 0x0f];
 	vma->vm_ops = NULL;
 	vma->vm_pgoff = pgoff;
 	vma->vm_file = NULL;
 	vma->vm_private_data = NULL;
-
-	/* Clear old maps */
-	error = -ENOMEM;
-	if (do_munmap(mm, addr, len))
-		goto free_vma;
-
-	/* Check against address space limit. */
-	if ((mm->total_vm << PAGE_SHIFT) + len
-	    > current->rlim[RLIMIT_AS].rlim_cur)
-		goto free_vma;
-
-	/* Private writable mapping? Check memory availability.. */
-	if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
-	    !(flags & MAP_NORESERVE)				 &&
-	    !vm_enough_memory(len >> PAGE_SHIFT))
-		goto free_vma;
+	vma->vm_raend = 0;
 
 	if (file) {
-		if (vma->vm_flags & VM_DENYWRITE) {
+		if (vm_flags & VM_DENYWRITE) {
 			error = deny_write_access(file);
 			if (error)
 				goto free_vma;
@@ -353,15 +355,15 @@
 	 * Answer: Yes, several device drivers can do it in their
 	 *         f_op->mmap method. -DaveM
 	 */
-	flags = vma->vm_flags;
 	addr = vma->vm_start;
 
 	insert_vm_struct(mm, vma);
 	if (correct_wcount)
 		atomic_inc(&file->f_dentry->d_inode->i_writecount);
-	
+
+out:	
 	mm->total_vm += len >> PAGE_SHIFT;
-	if (flags & VM_LOCKED) {
+	if (vm_flags & VM_LOCKED) {
 		mm->locked_vm += len >> PAGE_SHIFT;
 		make_pages_present(addr, addr + len);
 	}
@@ -825,12 +827,11 @@
 	if (!vm_enough_memory(len >> PAGE_SHIFT))
 		return -ENOMEM;
 
-	flags = vm_flags(PROT_READ|PROT_WRITE|PROT_EXEC,
+	flags = calc_vm_flags(PROT_READ|PROT_WRITE|PROT_EXEC,
 				MAP_FIXED|MAP_PRIVATE) | mm->def_flags;
 
 	flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
 	
-
 	/* Can we just expand an old anonymous mapping? */
 	if (addr) {
 		struct vm_area_struct * vma = find_vma(mm, addr-1);
@@ -841,7 +842,6 @@
 		}
 	}	
 
-
 	/*
 	 * create a vma struct for an anonymous mapping
 	 */
@@ -889,8 +889,8 @@
 	spin_lock(&mm->page_table_lock);
 	mpnt = mm->mmap;
 	mm->mmap = mm->mmap_avl = mm->mmap_cache = NULL;
-	spin_unlock(&mm->page_table_lock);
 	mm->rss = 0;
+	spin_unlock(&mm->page_table_lock);
 	mm->total_vm = 0;
 	mm->locked_vm = 0;
 
diff -urN linux-2.4.3-pre6/mm/swapfile.c linux/mm/swapfile.c
--- linux-2.4.3-pre6/mm/swapfile.c	Fri Feb  9 11:29:44 2001
+++ linux/mm/swapfile.c	Mon Dec  3 22:33:06 2001
@@ -209,6 +209,7 @@
  * share this swap entry, so be cautious and let do_wp_page work out
  * what to do if a write is requested later.
  */
+/* tasklist_lock and vma->vm_mm->page_table_lock are held */
 static inline void unuse_pte(struct vm_area_struct * vma, unsigned long address,
 	pte_t *dir, swp_entry_t entry, struct page* page)
 {
@@ -234,6 +235,7 @@
 	++vma->vm_mm->rss;
 }
 
+/* tasklist_lock and vma->vm_mm->page_table_lock are held */
 static inline void unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
 	unsigned long address, unsigned long size, unsigned long offset,
 	swp_entry_t entry, struct page* page)
@@ -261,6 +263,7 @@
 	} while (address && (address < end));
 }
 
+/* tasklist_lock and vma->vm_mm->page_table_lock are held */
 static inline void unuse_pgd(struct vm_area_struct * vma, pgd_t *dir,
 	unsigned long address, unsigned long size,
 	swp_entry_t entry, struct page* page)
@@ -291,6 +294,7 @@
 	} while (address && (address < end));
 }
 
+/* tasklist_lock and vma->vm_mm->page_table_lock are held */
 static void unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir,
 			swp_entry_t entry, struct page* page)
 {
diff -urN linux-2.4.3-pre6/mm/vmalloc.c linux/mm/vmalloc.c
--- linux-2.4.3-pre6/mm/vmalloc.c	Mon Dec  3 22:32:56 2001
+++ linux/mm/vmalloc.c	Mon Dec  3 22:33:06 2001
@@ -102,9 +102,11 @@
 		end = PMD_SIZE;
 	do {
 		struct page * page;
+		spin_unlock(&init_mm.page_table_lock);
+		page = alloc_page(gfp_mask);
+		spin_lock(&init_mm.page_table_lock);
 		if (!pte_none(*pte))
 			printk(KERN_ERR "alloc_area_pte: page already exists\n");
-		page = alloc_page(gfp_mask);
 		if (!page)
 			return -ENOMEM;
 		set_pte(pte, mk_pte(page, prot));
@@ -143,7 +145,7 @@
 
 	dir = pgd_offset_k(address);
 	flush_cache_all();
-	lock_kernel();
+	spin_lock(&init_mm.page_table_lock);
 	do {
 		pmd_t *pmd;
 		
@@ -161,7 +163,7 @@
 
 		ret = 0;
 	} while (address && (address < end));
-	unlock_kernel();
+	spin_unlock(&init_mm.page_table_lock);
 	flush_tlb_all();
 	return ret;
 }
diff -urN linux-2.4.3-pre6/mm/vmscan.c linux/mm/vmscan.c
--- linux-2.4.3-pre6/mm/vmscan.c	Mon Jan 15 12:36:49 2001
+++ linux/mm/vmscan.c	Mon Dec  3 22:33:06 2001
@@ -25,16 +25,15 @@
 #include <asm/pgalloc.h>
 
 /*
- * The swap-out functions return 1 if they successfully
- * threw something out, and we got a free page. It returns
- * zero if it couldn't do anything, and any other value
- * indicates it decreased rss, but the page was shared.
+ * The swap-out function returns 1 if it successfully
+ * scanned all the pages it was asked to (`count').
+ * It returns zero if it couldn't do anything,
  *
- * NOTE! If it sleeps, it *must* return 1 to make sure we
- * don't continue with the swap-out. Otherwise we may be
- * using a process that no longer actually exists (it might
- * have died while we slept).
+ * rss may decrease because pages are shared, but this
+ * doesn't count as having freed a page.
  */
+
+/* mm->page_table_lock is held. mmap_sem is not held */
 static void try_to_swap_out(struct mm_struct * mm, struct vm_area_struct* vma, unsigned long address, pte_t * page_table, struct page *page)
 {
 	pte_t pte;
@@ -129,6 +128,7 @@
 	return;
 }
 
+/* mm->page_table_lock is held. mmap_sem is not held */
 static int swap_out_pmd(struct mm_struct * mm, struct vm_area_struct * vma, pmd_t *dir, unsigned long address, unsigned long end, int count)
 {
 	pte_t * pte;
@@ -165,6 +165,7 @@
 	return count;
 }
 
+/* mm->page_table_lock is held. mmap_sem is not held */
 static inline int swap_out_pgd(struct mm_struct * mm, struct vm_area_struct * vma, pgd_t *dir, unsigned long address, unsigned long end, int count)
 {
 	pmd_t * pmd;
@@ -194,6 +195,7 @@
 	return count;
 }
 
+/* mm->page_table_lock is held. mmap_sem is not held */
 static int swap_out_vma(struct mm_struct * mm, struct vm_area_struct * vma, unsigned long address, int count)
 {
 	pgd_t *pgdir;
@@ -218,6 +220,9 @@
 	return count;
 }
 
+/*
+ * Returns non-zero if we scanned all `count' pages
+ */
 static int swap_out_mm(struct mm_struct * mm, int count)
 {
 	unsigned long address;
