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

diff -urN linux-2.4.15-pre8/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.4.15-pre8/Documentation/Configure.help	Mon Dec  3 23:32:34 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 23:32:45 2001
@@ -12742,6 +12742,17 @@
   The module will be called visor.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+USB IR Dongle Serial Driver
+CONFIG_USB_SERIAL_IR
+  Say Y here if you want to enable simple serial support for USB IrDA
+  devices.  This is useful if you do not want to use the full IrDA
+  stack.
+  
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called ir-usb.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
 USB Belkin and Paracom Single Port Serial Driver
 CONFIG_USB_SERIAL_BELKIN
   Say Y here if you want to use a Belkin USB Serial single port
diff -urN linux-2.4.15-pre8/Makefile linux/Makefile
--- linux-2.4.15-pre8/Makefile	Mon Dec  3 23:32:35 2001
+++ linux/Makefile	Mon Dec  3 23:32:45 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 15
-EXTRAVERSION =-pre8
+EXTRAVERSION =-pre9
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.15-pre8/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
--- linux-2.4.15-pre8/arch/alpha/kernel/smp.c	Mon Dec  3 23:32:35 2001
+++ linux/arch/alpha/kernel/smp.c	Mon Dec  3 23:32:45 2001
@@ -491,9 +491,9 @@
 		panic("idle process is init_task for CPU %d", cpuid);
 
 	idle->processor = cpuid;
+	idle->cpus_runnable = 1 << cpuid; /* we schedule the first task manually */
 	__cpu_logical_map[cpunum] = cpuid;
 	__cpu_number_map[cpuid] = cpunum;
-	idle->has_cpu = 1; /* we schedule the first task manually */
  
 	del_from_runqueue(idle);
 	unhash_process(idle);
diff -urN linux-2.4.15-pre8/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- linux-2.4.15-pre8/arch/i386/kernel/ptrace.c	Tue Sep 18 17:04:23 2001
+++ linux/arch/i386/kernel/ptrace.c	Mon Dec  3 23:32:45 2001
@@ -181,15 +181,11 @@
 		ret = ptrace_attach(child);
 		goto out_tsk;
 	}
-	ret = -ESRCH;
-	if (!(child->ptrace & PT_PTRACED))
-		goto out_tsk;
-	if (child->state != TASK_STOPPED) {
-		if (request != PTRACE_KILL)
-			goto out_tsk;
-	}
-	if (child->p_pptr != current)
+
+	ret = ptrace_check_attach(child, request == PTRACE_KILL);
+	if (ret < 0)
 		goto out_tsk;
+
 	switch (request) {
 	/* when I and D space are separate, these will need to be fixed. */
 	case PTRACE_PEEKTEXT: /* read word at location addr. */ 
diff -urN linux-2.4.15-pre8/arch/i386/kernel/smpboot.c linux/arch/i386/kernel/smpboot.c
--- linux-2.4.15-pre8/arch/i386/kernel/smpboot.c	Thu Oct  4 18:42:54 2001
+++ linux/arch/i386/kernel/smpboot.c	Mon Dec  3 23:32:45 2001
@@ -800,10 +800,10 @@
 		panic("No idle process for CPU %d", cpu);
 
 	idle->processor = cpu;
+	idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */
 
 	map_cpu_to_boot_apicid(cpu, apicid);
 
-	idle->has_cpu = 1; /* we schedule the first task manually */
 	idle->thread.eip = (unsigned long) start_secondary;
 
 	del_from_runqueue(idle);
diff -urN linux-2.4.15-pre8/arch/ia64/kernel/ptrace.c linux/arch/ia64/kernel/ptrace.c
--- linux-2.4.15-pre8/arch/ia64/kernel/ptrace.c	Mon Dec  3 23:32:35 2001
+++ linux/arch/ia64/kernel/ptrace.c	Mon Dec  3 23:32:45 2001
@@ -846,30 +846,10 @@
 		ret = ptrace_attach(child);
 		goto out_tsk;
 	}
-	ret = -ESRCH;
-	if (!(child->ptrace & PT_PTRACED))
-		goto out_tsk;
-
-	if (child->state != TASK_STOPPED) {
-		if (request != PTRACE_KILL)
-			goto out_tsk;
-	}
 
-	if (child->p_pptr != current)
+	ret = ptrace_check_attach(child, request == PTRACE_KILL);
+	if (ret < 0)
 		goto out_tsk;
-
-	if (request != PTRACE_KILL) {
-		if (child->state != TASK_STOPPED)
-			goto out_tsk;
-
-#ifdef CONFIG_SMP
-		while (child->has_cpu) {
-			if (child->state != TASK_STOPPED)
-				goto out_tsk;
-			barrier();
-		}
-#endif
-	}
 
 	pt = ia64_task_regs(child);
 	sw = (struct switch_stack *) (child->thread.ksp + 16);
diff -urN linux-2.4.15-pre8/arch/ia64/kernel/smpboot.c linux/arch/ia64/kernel/smpboot.c
--- linux-2.4.15-pre8/arch/ia64/kernel/smpboot.c	Mon Dec  3 23:32:35 2001
+++ linux/arch/ia64/kernel/smpboot.c	Mon Dec  3 23:32:45 2001
@@ -418,7 +418,7 @@
 
 	idle->processor = cpu;
 	ia64_cpu_to_sapicid[cpu] = sapicid;
-	idle->has_cpu = 1; /* we schedule the first task manually */
+	idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */
 
 	del_from_runqueue(idle);
 	unhash_process(idle);
diff -urN linux-2.4.15-pre8/arch/mips/kernel/smp.c linux/arch/mips/kernel/smp.c
--- linux-2.4.15-pre8/arch/mips/kernel/smp.c	Mon Jul  2 13:56:40 2001
+++ linux/arch/mips/kernel/smp.c	Mon Dec  3 23:32:45 2001
@@ -126,7 +126,7 @@
 
 		/* Schedule the first task manually */
 		p->processor = i;
-		p->has_cpu = 1;
+		p->cpus_runnable = 1 << i; /* we schedule the first task manually */
 
 		/* Attach to the address space of init_task. */
 		atomic_inc(&init_mm.mm_count);
@@ -155,7 +155,7 @@
 		sprintf(p->comm, "%s%d", "Idle", i);
 		init_tasks[i] = p;
 		p->processor = i;
-		p->has_cpu = 1; /* we schedule the first task manually */
+		p->cpus_runnable = 1 << i; /* we schedule the first task manually *
 		del_from_runqueue(p);
 		unhash_process(p);
 		/* Attach to the address space of init_task. */
diff -urN linux-2.4.15-pre8/arch/mips64/sgi-ip27/ip27-init.c linux/arch/mips64/sgi-ip27/ip27-init.c
--- linux-2.4.15-pre8/arch/mips64/sgi-ip27/ip27-init.c	Sun Sep  9 10:43:02 2001
+++ linux/arch/mips64/sgi-ip27/ip27-init.c	Mon Dec  3 23:32:45 2001
@@ -497,7 +497,7 @@
 			alloc_cpupda(cpu, num_cpus);
 			del_from_runqueue(p);
 			p->processor = num_cpus;
-			p->has_cpu = 1; /* we schedule the first task manually */
+			p->cpus_runnable = 1 << num_cpus; /* we schedule the first task manually */
 			unhash_process(p);
 			/* Attach to the address space of init_task. */
 			atomic_inc(&init_mm.mm_count);
diff -urN linux-2.4.15-pre8/arch/ppc/8xx_io/micropatch.c linux/arch/ppc/8xx_io/micropatch.c
--- linux-2.4.15-pre8/arch/ppc/8xx_io/micropatch.c	Sat Sep  8 12:39:33 2001
+++ linux/arch/ppc/8xx_io/micropatch.c	Mon Dec  3 23:32:45 2001
@@ -17,7 +17,7 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/8xx_immap.h>
-#include "commproc.h"
+#include <asm/commproc.h>
 
 /* Define this to get SMC patches as well.  You need to modify the uart
  * driver as well......
diff -urN linux-2.4.15-pre8/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- linux-2.4.15-pre8/arch/ppc/kernel/setup.c	Mon Dec  3 23:32:35 2001
+++ linux/arch/ppc/kernel/setup.c	Mon Dec  3 23:32:45 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.setup.c 1.63 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.setup.c 1.65 11/18/01 20:57:25 trini
  */
 /*
  * Common prep/pmac/chrp boot and setup code.
@@ -472,8 +472,8 @@
 			break;
 #ifdef CONFIG_BLK_DEV_INITRD
 		case BI_INITRD:
-			initrd_start = data[0];
-			initrd_end = data[0] + data[1];
+			initrd_start = data[0] + KERNELBASE;
+			initrd_end = data[0] + data[1] + KERNELBASE;
 			break;
 #endif /* CONFIG_BLK_DEV_INITRD */
 #ifdef CONFIG_ALL_PPC
diff -urN linux-2.4.15-pre8/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- linux-2.4.15-pre8/arch/ppc/kernel/smp.c	Fri Nov  2 17:43:54 2001
+++ linux/arch/ppc/kernel/smp.c	Mon Dec  3 23:32:45 2001
@@ -349,7 +349,7 @@
 		init_tasks[i] = p;
 
 		p->processor = i;
-		p->has_cpu = 1;
+		p->cpus_runnable = 1 << i; /* we schedule the first task manually */
 		current_set[i] = p;
 
 		/*
diff -urN linux-2.4.15-pre8/arch/s390/kernel/smp.c linux/arch/s390/kernel/smp.c
--- linux-2.4.15-pre8/arch/s390/kernel/smp.c	Thu Oct 11 09:04:57 2001
+++ linux/arch/s390/kernel/smp.c	Mon Dec  3 23:32:45 2001
@@ -531,7 +531,7 @@
         if (!idle)
                 panic("No idle process for CPU %d",cpu);
         idle->processor = cpu;
-        idle->has_cpu = 1; /* we schedule the first task manually */
+	idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */
 
         del_from_runqueue(idle);
         unhash_process(idle);
diff -urN linux-2.4.15-pre8/arch/s390x/kernel/smp.c linux/arch/s390x/kernel/smp.c
--- linux-2.4.15-pre8/arch/s390x/kernel/smp.c	Thu Oct 11 09:04:57 2001
+++ linux/arch/s390x/kernel/smp.c	Mon Dec  3 23:32:45 2001
@@ -510,7 +510,7 @@
         if (!idle)
                 panic("No idle process for CPU %d",cpu);
         idle->processor = cpu;
-        idle->has_cpu = 1; /* we schedule the first task manually */
+	idle->cpus_runnable = 1 << cpu; /* we schedule the first task manually */
 
         del_from_runqueue(idle);
         unhash_process(idle);
diff -urN linux-2.4.15-pre8/arch/sparc/kernel/sun4d_smp.c linux/arch/sparc/kernel/sun4d_smp.c
--- linux-2.4.15-pre8/arch/sparc/kernel/sun4d_smp.c	Fri Feb  9 11:37:03 2001
+++ linux/arch/sparc/kernel/sun4d_smp.c	Mon Dec  3 23:32:45 2001
@@ -225,7 +225,7 @@
 			init_tasks[i] = p;
 
 			p->processor = i;
-			p->has_cpu = 1; /* we schedule the first task manually */
+			p->cpus_runnable = 1 << i; /* we schedule the first task manually */
 
 			current_set[i] = p;
 
diff -urN linux-2.4.15-pre8/arch/sparc/kernel/sun4m_smp.c linux/arch/sparc/kernel/sun4m_smp.c
--- linux-2.4.15-pre8/arch/sparc/kernel/sun4m_smp.c	Fri Feb  9 11:37:03 2001
+++ linux/arch/sparc/kernel/sun4m_smp.c	Mon Dec  3 23:32:45 2001
@@ -198,7 +198,7 @@
 			init_tasks[i] = p;
 
 			p->processor = i;
-			p->has_cpu = 1; /* we schedule the first task manually */
+			p->cpus_runnable = 1 << i; /* we schedule the first task manually */
 
 			current_set[i] = p;
 
diff -urN linux-2.4.15-pre8/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- linux-2.4.15-pre8/arch/sparc64/kernel/smp.c	Mon Dec  3 23:32:35 2001
+++ linux/arch/sparc64/kernel/smp.c	Mon Dec  3 23:32:45 2001
@@ -276,7 +276,7 @@
 			init_tasks[cpucount] = p;
 
 			p->processor = i;
-			p->has_cpu = 1; /* we schedule the first task manually */
+			p->cpus_runnable = 1 << i; /* we schedule the first task manually */
 
 			del_from_runqueue(p);
 			unhash_process(p);
diff -urN linux-2.4.15-pre8/drivers/hotplug/pci_hotplug_core.c linux/drivers/hotplug/pci_hotplug_core.c
--- linux-2.4.15-pre8/drivers/hotplug/pci_hotplug_core.c	Mon Dec  3 23:32:35 2001
+++ linux/drivers/hotplug/pci_hotplug_core.c	Mon Dec  3 23:32:45 2001
@@ -622,7 +622,7 @@
 static ssize_t power_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset)
 {
 	struct hotplug_slot *slot = file->private_data;
-	const char *buff;
+	char *buff;
 	unsigned long lpower;
 	u8 power;
 	int retval = 0;
@@ -639,10 +639,11 @@
 		return -ENODEV;
 	}
 
-	buff = kmalloc (count, GFP_KERNEL);
+	buff = kmalloc (count + 1, GFP_KERNEL);
 	if (!buff)
 		return -ENOMEM;
-
+	memset (buff, 0x00, count + 1);
+ 
 	if (copy_from_user ((void *)buff, (void *)ubuff, count)) {
 		retval = -EFAULT;
 		goto exit;
@@ -732,7 +733,7 @@
 static ssize_t attention_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset)
 {
 	struct hotplug_slot *slot = file->private_data;
-	const char *buff;
+	char *buff;
 	unsigned long lattention;
 	u8 attention;
 	int retval = 0;
@@ -749,9 +750,10 @@
 		return -ENODEV;
 	}
 
-	buff = kmalloc (count, GFP_KERNEL);
+	buff = kmalloc (count + 1, GFP_KERNEL);
 	if (!buff)
 		return -ENOMEM;
+	memset (buff, 0x00, count + 1);
 
 	if (copy_from_user ((void *)buff, (void *)ubuff, count)) {
 		retval = -EFAULT;
@@ -868,7 +870,7 @@
 static ssize_t test_write_file (struct file *file, const char *ubuff, size_t count, loff_t *offset)
 {
 	struct hotplug_slot *slot = file->private_data;
-	const char *buff;
+	char *buff;
 	unsigned long ltest;
 	u32 test;
 	int retval = 0;
@@ -885,9 +887,10 @@
 		return -ENODEV;
 	}
 
-	buff = kmalloc (count, GFP_KERNEL);
+	buff = kmalloc (count + 1, GFP_KERNEL);
 	if (!buff)
 		return -ENOMEM;
+	memset (buff, 0x00, count + 1);
 
 	if (copy_from_user ((void *)buff, (void *)ubuff, count)) {
 		retval = -EFAULT;
diff -urN linux-2.4.15-pre8/drivers/pcmcia/yenta.c linux/drivers/pcmcia/yenta.c
--- linux-2.4.15-pre8/drivers/pcmcia/yenta.c	Mon Dec  3 23:32:35 2001
+++ linux/drivers/pcmcia/yenta.c	Mon Dec  3 23:32:45 2001
@@ -770,6 +770,7 @@
 {
 	/* Disable all events so we don't die in an IRQ storm */
 	cb_writel(sock, CB_SOCKET_MASK, 0x0);
+	exca_writeb(sock, I365_CSCINT, 0);
 
 	if (sock->cb_irq)
 		free_irq(sock->cb_irq, sock);
diff -urN linux-2.4.15-pre8/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux-2.4.15-pre8/drivers/scsi/Makefile	Mon Dec  3 23:32:35 2001
+++ linux/drivers/scsi/Makefile	Mon Dec  3 23:32:45 2001
@@ -175,6 +175,8 @@
 
 53c8xx_u.h: 53c8xx_d.h
 
+53c7,8xx.o: 53c8xx_u.h
+
 53c7xx_d.h: 53c7xx.scr script_asm.pl
 	ln -sf 53c7xx.scr fake7.c
 	$(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | $(PERL) -s script_asm.pl -ncr7x0_family
diff -urN linux-2.4.15-pre8/drivers/scsi/aic7xxx/aic7xxx_linux.c linux/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- linux-2.4.15-pre8/drivers/scsi/aic7xxx/aic7xxx_linux.c	Thu Oct 25 13:53:49 2001
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux.c	Mon Dec  3 23:32:46 2001
@@ -129,6 +129,7 @@
 #include "../sd.h"		/* For geometry detection */
 
 #include <linux/mm.h>		/* For fetching system memory size */
+#include <linux/blk.h>
 
 /*
  * To generate the correct addresses for the controller to issue
@@ -2742,7 +2743,7 @@
 	struct	buffer_head *bh;
 
 	ahc = *((struct ahc_softc **)disk->device->host->hostdata);
-	bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+	bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, block_size(dev));
 
 	if (bh) {
 		ret = scsi_partsize(bh, disk->capacity,
diff -urN linux-2.4.15-pre8/drivers/scsi/aic7xxx_old.c linux/drivers/scsi/aic7xxx_old.c
--- linux-2.4.15-pre8/drivers/scsi/aic7xxx_old.c	Thu Oct 11 09:43:30 2001
+++ linux/drivers/scsi/aic7xxx_old.c	Mon Dec  3 23:32:46 2001
@@ -238,6 +238,7 @@
 #include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/smp.h>
+#include <linux/blk.h>
 #include "sd.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -11739,7 +11740,7 @@
   struct buffer_head *bh;
 
   p = (struct aic7xxx_host *) disk->device->host->hostdata;
-  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024);
+  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, block_size(dev));
 
   if ( bh )
   {
diff -urN linux-2.4.15-pre8/drivers/usb/serial/io_edgeport.c linux/drivers/usb/serial/io_edgeport.c
--- linux-2.4.15-pre8/drivers/usb/serial/io_edgeport.c	Mon Dec  3 23:32:35 2001
+++ linux/drivers/usb/serial/io_edgeport.c	Mon Dec  3 23:32:46 2001
@@ -25,6 +25,14 @@
  *
  * Version history:
  * 
+ * 2.2 2001_11_14 greg kroah-hartman
+ *	- fixed bug in edge_close that kept the port from being used more
+ *	  than once.
+ *	- fixed memory leak on device removal.
+ *	- fixed potential double free of memory when command urb submitting
+ *	  failed.
+ *	- other small cleanups when the device is removed
+ *	
  * 2.1 2001_07_09 greg kroah-hartman
  *	- added support for TIOCMBIS and TIOCMBIC.
  *
@@ -263,7 +271,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v2.1"
+#define DRIVER_VERSION "v2.2"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli"
 #define DRIVER_DESC "Edgeport USB Serial Driver"
 
@@ -433,7 +441,7 @@
 
 /* function prototypes for all URB callbacks */
 static void edge_interrupt_callback	(struct urb *urb);
-static void edge_bulk_in_callback		(struct urb *urb);
+static void edge_bulk_in_callback	(struct urb *urb);
 static void edge_bulk_out_data_callback	(struct urb *urb);
 static void edge_bulk_out_cmd_callback	(struct urb *urb);
 
@@ -646,12 +654,6 @@
 }
 #endif
 
-
-
-/************************************************************************
- *																		*
- *																		*
- ************************************************************************/
 static void get_product_info(struct edgeport_serial *edge_serial)
 {
 	struct edgeport_product_info *product_info = &edge_serial->product_info;
@@ -933,7 +935,7 @@
 	dbg(__FUNCTION__" - FREE URB %p (outstanding %d)", urb, CmdUrbs);
 
 
-	/* if this urb had a transfer buffer already (old transfer) free it */
+	/* clean up the transfer buffer */
 	if (urb->transfer_buffer != NULL) {
 		kfree(urb->transfer_buffer);
 	}
@@ -957,7 +959,6 @@
 	/* tell the tty driver that something has changed */
 	wake_up_interruptible(&tty->write_wait);
 
-
 	/* we have completed the command */
 	edge_port->commandPending = FALSE;
 	wake_up_interruptible(&edge_port->wait_command);
@@ -987,6 +988,9 @@
 	
 	dbg(__FUNCTION__ " - port %d", port->number);
 
+	if (edge_port == NULL)
+		return -ENODEV;
+
 	++port->open_count;
 	MOD_INC_USE_COUNT;
 	
@@ -1002,6 +1006,12 @@
 		   as the structures were not set up at that time.) */
 		serial = port->serial;
 		edge_serial = (struct edgeport_serial *)serial->private;
+		if (edge_serial == NULL) {
+			port->active = 0;
+			port->open_count = 0;
+			MOD_DEC_USE_COUNT;
+			return -ENODEV;
+		}
 		if (edge_serial->interrupt_in_buffer == NULL) {
 			struct usb_serial_port *port0 = &serial->port[0];
 			
@@ -1015,27 +1025,22 @@
 			edge_serial->bulk_out_endpoint = port0->bulk_out_endpointAddress;
 		
 			/* set up our interrupt urb */
-			/* Like to use FILL_INT_URB, but we don't know wMaxPacketSize or bInterval, something to change for 2.5... */
-			edge_serial->interrupt_read_urb->complete = edge_interrupt_callback;
-			edge_serial->interrupt_read_urb->context = edge_serial;
-			edge_serial->interrupt_read_urb->dev = serial->dev;
-			/* FILL_INT_URB(edge_serial->interrupt_read_urb, serial->dev, 
-				     usb_rcvintpipe (serial->dev, edge_serial->interrupt_in_endpoint),
-				     edge_serial->interrupt_in_buffer, edge_serial->interrupt_in_endpoint.wMaxPacketSize,
-				     edge_interrupt_callback, edge_serial, edge_serial->interrupt_in_endpoint.bInterval);
-			*/
+			FILL_INT_URB(edge_serial->interrupt_read_urb,
+				     serial->dev,
+				     usb_rcvintpipe(serial->dev,
+					            port0->interrupt_in_endpointAddress),
+				     port0->interrupt_in_buffer,
+				     edge_serial->interrupt_read_urb->transfer_buffer_length,
+				     edge_interrupt_callback, edge_serial,
+				     edge_serial->interrupt_read_urb->interval);
 			
 			/* set up our bulk in urb */
-			/* Like to use FILL_BULK_URB, but we don't know wMaxPacketSize or bInterval, something to change for 2.5... */
-			edge_serial->read_urb->complete = edge_bulk_in_callback;
-			edge_serial->read_urb->context = edge_serial;
-			edge_serial->read_urb->dev = serial->dev;
-			/* FILL_BULK_URB(edge_serial->read_urb, serial->dev, 
-				      usb_rcvbulkpipe (serial->dev, port->bulk_in_endpointAddress),
-				      edge_serial->bulk_in_buffer, edge_serial->bulk_in_endpoint->wMaxPacketSize,
+			FILL_BULK_URB(edge_serial->read_urb, serial->dev,
+				      usb_rcvbulkpipe(serial->dev, port0->bulk_in_endpointAddress),
+				      port0->bulk_in_buffer,
+				      edge_serial->read_urb->transfer_buffer_length,
 				      edge_bulk_in_callback, edge_serial);
-			*/
-		
+
 			/* start interrupt read for this edgeport
 			 * this interrupt will continue as long as the edgeport is connected */
 			response = usb_submit_urb (edge_serial->interrupt_read_urb);
@@ -1239,7 +1244,9 @@
 	
 	edge_serial = (struct edgeport_serial *)serial->private;
 	edge_port = (struct edgeport_port *)port->private;
-
+	if ((edge_serial == NULL) || (edge_port == NULL))
+		return;
+	
 	--port->open_count;
 
 	if (port->open_count <= 0) {
@@ -1285,15 +1292,14 @@
 		if (edge_port->txfifo.fifo) {
 			kfree(edge_port->txfifo.fifo);
 		}
+		port->active = 0;
+		port->open_count = 0;
 	}
 
 	MOD_DEC_USE_COUNT;
 	dbg(__FUNCTION__" exited");
 }   
 
-
-
-
 /*****************************************************************************
  * SerialWrite
  *	this function is called by the tty driver when data should be written to
@@ -1312,6 +1318,9 @@
 
 	dbg(__FUNCTION__ " - port %d", port->number);
 
+	if (edge_port == NULL)
+		return -ENODEV;
+
 	// get a pointer to the Tx fifo
 	fifo = &edge_port->txfifo;
 
@@ -1509,9 +1518,10 @@
 
 	dbg(__FUNCTION__);
 
-	if (edge_port->closePending == TRUE) {
+	if (edge_port == NULL)
+		return -ENODEV;
+	if (edge_port->closePending == TRUE)
 		return -ENODEV;
-	}
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
@@ -1544,6 +1554,11 @@
 
 	dbg(__FUNCTION__);
 
+	if (edge_port == NULL)
+		return -ENODEV;
+	if (edge_port->closePending == TRUE)
+		return -ENODEV;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return -EINVAL;
@@ -1571,6 +1586,9 @@
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
+	if (edge_port == NULL)
+		return;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return;
@@ -1613,6 +1631,9 @@
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
+	if (edge_port == NULL)
+		return;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return;
@@ -1670,6 +1691,9 @@
 
 	dbg(__FUNCTION__" - port %d", port->number);
 
+	if (edge_port == NULL)
+		return;
+
 	if (!edge_port->open) {
 		dbg (__FUNCTION__" - port not opened");
 		return;
@@ -2457,15 +2481,12 @@
 
 	/* Allocate our next urb */
 	urb = usb_alloc_urb (0);
+	if (!urb)
+		return -ENOMEM;
 
 	CmdUrbs++;
-
 	dbg(__FUNCTION__" - ALLOCATE URB %p (outstanding %d)", urb, CmdUrbs);
 
-	if (!urb) {
-		return -ENOMEM;
-	}
-
 	FILL_BULK_URB (urb, edge_serial->serial->dev, 
 		       usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint),
 		       buffer, length, edge_bulk_out_cmd_callback, edge_port);
@@ -2474,17 +2495,11 @@
 	urb->transfer_flags |= USB_QUEUE_BULK;
 
 	edge_port->commandPending = TRUE;
-	urb->dev = edge_serial->serial->dev;
 	status = usb_submit_urb(urb);
 
 	if (status) {
 		/* something went wrong */
 		dbg(__FUNCTION__" - usb_submit_urb(write bulk) failed");
-
-		/* if this urb had a transfer buffer already (old transfer) free it */
-		if (urb->transfer_buffer != NULL) {
-			kfree(urb->transfer_buffer);
-		}
 		usb_unlink_urb (urb);
 		usb_free_urb   (urb);
 		return status;
@@ -2548,6 +2563,10 @@
 	MAKE_CMD_WRITE_REG( &currCmd, &cmdLen, number, LCR, edge_port->shadowLCR);
 
 	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen );
+	if (status) {
+		/* something bad happened, let's free up the memory */
+		kfree (cmdBuffer);
+	}
 
 	return status;
 }
@@ -2623,6 +2642,10 @@
 	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, edge_port->port->number, regNum, regValue);
 
 	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
+	if (status) {
+		/* something bad happened, let's free up the memory */
+		kfree (cmdBuffer);
+	}
 
 	return status;
 }
@@ -2707,10 +2730,8 @@
 		unsigned char stop_char  = STOP_CHAR(tty);
 		unsigned char start_char = START_CHAR(tty);
 
-		{
-			send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char);
-			send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char);
-		}
+		send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char);
+		send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char);
 
 		/* if we are implementing INBOUND XON/XOFF */
 		if (I_IXOFF(tty)) {
@@ -2962,10 +2983,11 @@
 	get_product_info(edge_serial);
 
 	/* set the number of ports from the manufacturing description */
-	// FIXME  should we override this???
-	//serial->num_ports = serial->product_info.NumPorts;
+	/* serial->num_ports = serial->product_info.NumPorts; */
 	if (edge_serial->product_info.NumPorts != serial->num_ports) {
-		warn(__FUNCTION__ " - Device Reported %d serial ports vs core thinking we have %d ports, email greg@kroah.com this info.", edge_serial->product_info.NumPorts, serial->num_ports);
+		warn(__FUNCTION__ " - Device Reported %d serial ports vs core "
+		     "thinking we have %d ports, email greg@kroah.com this info.",
+		     edge_serial->product_info.NumPorts, serial->num_ports);
 	}
 
 	dbg(__FUNCTION__ " - time 1 %ld", jiffies);
@@ -3005,10 +3027,9 @@
 
 
 /****************************************************************************
- * usb_edgeport_disconnect
+ * edge_shutdown
  *	This function is called whenever the device is removed from the usb bus.
  ****************************************************************************/
-//static void usb_edgeport_disconnect (struct usb_device *dev, void *ptr)
 static void edge_shutdown (struct usb_serial *serial)
 {
 	int i;
@@ -3020,11 +3041,11 @@
 		while (serial->port[i].open_count > 0) {
 			edge_close (&serial->port[i], NULL);
 		}
+		kfree (serial->port[i].private);
+		serial->port[i].private = NULL;
 	}
-
-	/* free up any memory that we allocated */
-	// FIXME
-	
+	kfree (serial->private);
+	serial->private = NULL;
 }
 
 
diff -urN linux-2.4.15-pre8/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux-2.4.15-pre8/drivers/usb/usb.c	Sat Oct 20 19:13:11 2001
+++ linux/drivers/usb/usb.c	Mon Dec  3 23:32:46 2001
@@ -1019,7 +1019,7 @@
 /*-------------------------------------------------------------------*/
 int usb_submit_urb(urb_t *urb)
 {
-	if (urb && urb->dev)
+	if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
 		return urb->dev->bus->op->submit_urb(urb);
 	else
 		return -ENODEV;
@@ -1028,7 +1028,7 @@
 /*-------------------------------------------------------------------*/
 int usb_unlink_urb(urb_t *urb)
 {
-	if (urb && urb->dev)
+	if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
 		return urb->dev->bus->op->unlink_urb(urb);
 	else
 		return -ENODEV;
diff -urN linux-2.4.15-pre8/drivers/usb/usbnet.c linux/drivers/usb/usbnet.c
--- linux-2.4.15-pre8/drivers/usb/usbnet.c	Mon Dec  3 23:32:35 2001
+++ linux/drivers/usb/usbnet.c	Mon Dec  3 23:32:46 2001
@@ -16,11 +16,10 @@
  *
  *	- AnchorChip 2720
  *	- Belkin, eTEK (interops with Win32 drivers)
+ *	- GeneSys GL620USB-A
  *	- "Linux Devices" (like iPaq and similar SA-1100 based PDAs)
  *	- NetChip 1080 (interoperates with NetChip Win32 drivers)
  *	- Prolific PL-2301/2302 (replaces "plusb" driver)
- *	- GeneSys GL620USB-A
-
  *
  * USB devices can implement their side of this protocol at the cost
  * of two bulk endpoints; it's not restricted to "cable" applications.
@@ -78,9 +77,11 @@
  * 17-oct-2001	Handle "Advance USBNET" product, like Belkin/eTEK devices,
  *		from Ioannis Mavroukakis <i.mavroukakis@btinternet.com>;
  *		rx unlinks somehow weren't async; minor cleanup.
- * 25-oct-2001	Merged GeneSys driver, using code from
- *		Jiun-Jie Huang <huangjj@genesyslogic.com.tw>
- *		by Stanislav Brabec <utx@penguin.cz>
+ * 03-nov-2001	Merged GeneSys driver; original code from Jiun-Jie Huang
+ *		<huangjj@genesyslogic.com.tw>, updated by Stanislav Brabec
+ *		<utx@penguin.cz>.  Made framing options (NetChip/GeneSys)
+ *		tie mostly to (sub)driver info.  Workaround some PL-2302
+ *		chips that seem to reject SET_INTERFACE requests.
  *
  *-------------------------------------------------------------------------*/
 
@@ -106,10 +107,10 @@
 
 #define	CONFIG_USB_AN2720
 #define	CONFIG_USB_BELKIN
+#define	CONFIG_USB_GENESYS
 #define	CONFIG_USB_LINUXDEV
 #define	CONFIG_USB_NET1080
 #define	CONFIG_USB_PL2301
-#define	CONFIG_USB_GENELINK
 
 
 /*-------------------------------------------------------------------------*/
@@ -163,7 +164,10 @@
 	// protocol/interface state
 	struct net_device	net;
 	struct net_device_stats	stats;
+
+#ifdef CONFIG_USB_NET1080
 	u16			packet_id;
+#endif
 
 	// various kinds of pending driver work
 	struct sk_buff_head	rxq;
@@ -171,9 +175,6 @@
 	struct sk_buff_head	done;
 	struct tasklet_struct	bh;
 	struct tq_struct	ctrl_task;
-
-	// various data structure may be needed
-	void			*priv_data;
 };
 
 // device-specific info used by the driver
@@ -182,7 +183,7 @@
 
 	int		flags;
 #define FLAG_FRAMING_NC	0x0001		/* guard against device dropouts */ 
-#define FLAG_GENELINK	0x0002		/* genelink flag */
+#define FLAG_FRAMING_GL	0x0002		/* genelink batches packets */
 #define FLAG_NO_SETINT	0x0010		/* device can't set_interface() */
 
 	/* reset device ... can sleep */
@@ -191,13 +192,15 @@
 	/* see if peer is connected ... can sleep */
 	int	(*check_connect)(struct usbnet *);
 
-	/* allocate and initialize the private resources per device */
-	int	(*initialize_private)(struct usbnet *);
+	/* fixup rx packet (strip framing) */
+	int	(*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
 
-	/* free the private resources per device */
-	int	(*release_private)(struct usbnet *);
+	/* fixup tx packet (add framing) */
+	struct sk_buff	*(*tx_fixup)(struct usbnet *dev,
+				struct sk_buff *skb, int flags);
 
 	// FIXME -- also an interrupt mechanism
+	// useful for at least PL2301/2302 and GL620USB-A
 
 	/* framework currently "knows" bulk EPs talk packets */
 	int		in;		/* rx endpoint */
@@ -240,47 +243,6 @@
 #define devinfo(usbnet, fmt, arg...) \
 	printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name, ## arg)
 
-/*-------------------------------------------------------------------------
- *
- * NetChip framing of ethernet packets, supporting additional error
- * checks for links that may drop bulk packets from inside messages.
- * Odd USB length == always short read for last usb packet.
- *	- nc_header
- *	- Ethernet header (14 bytes)
- *	- payload
- *	- (optional padding byte, if needed so length becomes odd)
- *	- nc_trailer
- *
- * This framing is to be avoided for non-NetChip devices.
- */
-
-struct nc_header {		// packed:
-	u16	hdr_len;		// sizeof nc_header (LE, all)
-	u16	packet_len;		// payload size (including ethhdr)
-	u16	packet_id;		// detects dropped packets
-#define MIN_HEADER	6
-
-	// all else is optional, and must start with:
-	// u16	vendorId;		// from usb-if
-	// u16	productId;
-} __attribute__((__packed__));
-
-#define	PAD_BYTE	((unsigned char)0xAC)
-
-struct nc_trailer {
-	u16	packet_id;
-} __attribute__((__packed__));
-
-// packets may use FLAG_FRAMING_NC and optional pad
-#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \
-				+ sizeof (struct ethhdr) \
-				+ (mtu) \
-				+ 1 \
-				+ sizeof (struct nc_trailer))
-
-#define MIN_FRAMED	FRAMED_SIZE(0)
-
-
 
 #ifdef	CONFIG_USB_AN2720
 
@@ -329,6 +291,330 @@
 
 
 
+#ifdef CONFIG_USB_GENESYS
+
+/*-------------------------------------------------------------------------
+ *
+ * GeneSys GL620USB-A (www.genesyslogic.com.tw)
+ *
+ * ... should partially interop with the Win32 driver for this hardware
+ * The GeneSys docs imply there's some NDIS issue motivating this framing.
+ *
+ *-------------------------------------------------------------------------*/
+
+// control msg write command
+#define GENELINK_CONNECT_WRITE			0xF0
+// interrupt pipe index
+#define GENELINK_INTERRUPT_PIPE			0x03
+// interrupt read buffer size
+#define INTERRUPT_BUFSIZE			0x08
+// interrupt pipe interval value
+#define GENELINK_INTERRUPT_INTERVAL		0x10
+// max transmit packet number per transmit
+#define GL_MAX_TRANSMIT_PACKETS			32
+// max packet length
+#define GL_MAX_PACKET_LEN			1514
+// max receive buffer size 
+#define GL_RCV_BUF_SIZE		\
+	(((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4)
+
+struct gl_packet {
+	u32		packet_length;
+	char		packet_data [1];
+};
+
+struct gl_header {
+	u32			packet_count;
+	struct gl_packet	packets;
+};
+
+#ifdef	GENLINK_ACK
+
+// FIXME:  this code is incomplete, not debugged; it doesn't
+// handle interrupts correctly.  interrupts should be generic
+// code like all other device I/O, anyway.
+
+struct gl_priv { 
+	struct urb	*irq_urb;
+	char		irq_buf [INTERRUPT_BUFSIZE];
+};
+
+static inline int gl_control_write (struct usbnet *dev, u8 request, u16 value)
+{
+	int retval;
+
+	retval = usb_control_msg (dev->udev,
+		      usb_sndctrlpipe (dev->udev, 0),
+		      request,
+		      USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		      value, 
+		      0,			// index
+		      0,			// data buffer
+		      0,			// size
+		      CONTROL_TIMEOUT_JIFFIES);
+	return retval;
+}
+
+static void gl_interrupt_complete (struct urb *urb)
+{
+	int status = urb->status;
+	
+	if (status)
+		dbg ("gl_interrupt_complete fail - %X", status);
+	else
+		dbg ("gl_interrupt_complete success...");
+}
+
+static int gl_interrupt_read (struct usbnet *dev)
+{
+	struct gl_priv	*priv = dev->priv_data;
+	int		retval;
+
+	// issue usb interrupt read
+	if (priv && priv->irq_urb) {
+		// submit urb
+		if ((retval = usb_submit_urb (priv->irq_urb)) != 0)
+			dbg ("gl_interrupt_read: submit fail - %X...", retval);
+		else
+			dbg ("gl_interrupt_read: submit success...");
+	}
+
+	return 0;
+}
+
+// check whether another side is connected
+static int genelink_check_connect (struct usbnet *dev)
+{
+	int			retval;
+
+	dbg ("genelink_check_connect...");
+
+	// detect whether another side is connected
+	if ((retval = gl_control_write (dev, GENELINK_CONNECT_WRITE, 0)) != 0) {
+		dbg ("%s: genelink_check_connect write fail - %X",
+			dev->net.name, retval);
+		return retval;
+	}
+
+	// usb interrupt read to ack another side 
+	if ((retval = gl_interrupt_read (dev)) != 0) {
+		dbg ("%s: genelink_check_connect read fail - %X",
+			dev->net.name, retval);
+		return retval;
+	}
+
+	dbg ("%s: genelink_check_connect read success", dev->net.name);
+	return 0;
+}
+
+// allocate and initialize the private data for genelink
+static int genelink_init (struct usbnet *dev)
+{
+	struct gl_priv *priv;
+
+	// allocate the private data structure
+	if ((priv = kmalloc (sizeof *priv, GFP_KERNEL)) == 0) {
+		dbg ("%s: cannot allocate private data per device",
+			dev->net.name);
+		return -ENOMEM;
+	}
+
+	// allocate irq urb
+	if ((priv->irq_urb = usb_alloc_urb (0)) == 0) {
+		dbg ("%s: cannot allocate private irq urb per device",
+			dev->net.name);
+		kfree (priv);
+		return -ENOMEM;
+	}
+
+	// fill irq urb
+	FILL_INT_URB (priv->irq_urb, dev->udev,
+		usb_rcvintpipe (dev->udev, GENELINK_INTERRUPT_PIPE),
+		priv->irq_buf, INTERRUPT_BUFSIZE,
+		gl_interrupt_complete, 0,
+		GENELINK_INTERRUPT_INTERVAL);
+
+	// set private data pointer
+	dev->priv_data = priv;
+
+	return 0;
+}
+
+// release the private data
+static int genelink_free (struct usbnet *dev)
+{
+	struct gl_priv	*priv = dev->priv_data;
+
+	if (!priv) 
+		return 0;
+
+// FIXME:  can't cancel here; it's synchronous, and
+// should have happened earlier in any case (interrupt
+// handling needs to be generic)
+
+	// cancel irq urb first
+	usb_unlink_urb (priv->irq_urb);
+
+	// free irq urb
+	usb_free_urb (priv->irq_urb);
+
+	// free the private data structure
+	kfree (priv);
+
+	return 0;
+}
+
+#else
+
+static int genelink_check_connect (struct usbnet *dev)
+{
+	dbg ("%s: assuming peer is connected", dev->net.name);
+	return 0;
+}
+
+#endif
+
+// reset the device status
+static int genelink_reset (struct usbnet *dev)
+{
+	// we don't need to reset, just return 0
+	return 0;
+}
+
+static int genelink_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
+{
+	struct gl_header	*header;
+	struct gl_packet	*packet;
+	struct sk_buff		*gl_skb;
+	int			status;
+	u32			size;
+
+	header = (struct gl_header *) skb->data;
+
+	// get the packet count of the received skb
+	le32_to_cpus (&header->packet_count);
+	if ((header->packet_count > GL_MAX_TRANSMIT_PACKETS)
+			|| (header->packet_count < 0)) {
+		dbg ("genelink: illegal received packet count %d",
+			header->packet_count);
+		return 0;
+	}
+
+	// set the current packet pointer to the first packet
+	packet = &header->packets;
+
+	// decrement the length for the packet count size 4 bytes
+	skb_pull (skb, 4);
+
+	while (header->packet_count > 1) {
+		// get the packet length
+		size = packet->packet_length;
+
+		// this may be a broken packet
+		if (size > GL_MAX_PACKET_LEN) {
+			dbg ("genelink: illegal rx length %d", size);
+			return 0;
+		}
+
+		// allocate the skb for the individual packet
+		gl_skb = alloc_skb (size, GFP_ATOMIC);
+		if (gl_skb == 0)
+			return 0;
+
+		// copy the packet data to the new skb
+		memcpy (gl_skb->data, packet->packet_data, size);
+
+		// set skb data size
+		gl_skb->len = size;
+		gl_skb->dev = &dev->net;
+
+		// determine the packet's protocol ID
+		gl_skb->protocol = eth_type_trans (gl_skb, &dev->net);
+
+		// update the status
+		dev->stats.rx_packets++;
+		dev->stats.rx_bytes += size;
+
+		// notify os of the received packet
+		status = netif_rx (gl_skb);
+
+		// advance to the next packet
+		packet = (struct gl_packet *)
+			&packet->packet_data [size];
+		header->packet_count--;
+
+		// shift the data pointer to the next gl_packet
+		skb_pull (skb, size + 4);
+	}
+
+	// skip the packet length field 4 bytes
+	skb_pull (skb, 4);
+
+	if (skb->len > GL_MAX_PACKET_LEN) {
+		dbg ("genelink: illegal rx length %d", skb->len);
+		return 0;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+genelink_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
+{
+	int 	padlen;
+	int	length = skb->len;
+	int	headroom = skb_headroom (skb);
+	int	tailroom = skb_tailroom (skb);
+	u32	*packet_count;
+	u32	*packet_len;
+
+	// FIXME:  magic numbers, bleech
+	padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1;
+
+	if ((!skb_cloned (skb))
+			&& ((headroom + tailroom) >= (padlen + (4 + 4*1)))) {
+		if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
+			skb->data = memmove (skb->head + (4 + 4*1),
+					     skb->data, skb->len);
+			skb->tail = skb->data + skb->len;
+		}
+	} else {
+		struct sk_buff	*skb2;
+		skb2 = skb_copy_expand (skb, (4 + 4*1) , padlen, flags);
+		dev_kfree_skb_any (skb);
+		skb = skb2;
+	}
+
+	// attach the packet count to the header
+	packet_count = (u32 *) skb_push (skb, (4 + 4*1));
+	packet_len = packet_count + 1;
+
+	// FIXME little endian?
+	*packet_count = 1;
+	*packet_len = length;
+
+	// add padding byte
+	if ((skb->len % EP_SIZE (dev)) == 0)
+		skb_put (skb, 1);
+
+	return skb;
+}
+
+static const struct driver_info	genelink_info = {
+	description:	"Genesys GeneLink",
+	flags:		FLAG_FRAMING_GL | FLAG_NO_SETINT,
+	reset:		genelink_reset,
+	check_connect:	genelink_check_connect,
+	rx_fixup:	genelink_rx_fixup,
+	tx_fixup:	genelink_tx_fixup,
+
+	in: 1, out: 2,
+	epsize:	64,
+};
+
+#endif /* CONFIG_USB_GENESYS */
+
+
+
 #ifdef	CONFIG_USB_LINUXDEV
 
 /*-------------------------------------------------------------------------
@@ -364,10 +650,51 @@
 /*-------------------------------------------------------------------------
  *
  * Netchip 1080 driver ... http://www.netchip.com
+ * Used in LapLink cables
  *
  *-------------------------------------------------------------------------*/
 
 /*
+ * NetChip framing of ethernet packets, supporting additional error
+ * checks for links that may drop bulk packets from inside messages.
+ * Odd USB length == always short read for last usb packet.
+ *	- nc_header
+ *	- Ethernet header (14 bytes)
+ *	- payload
+ *	- (optional padding byte, if needed so length becomes odd)
+ *	- nc_trailer
+ *
+ * This framing is to be avoided for non-NetChip devices.
+ */
+
+struct nc_header {		// packed:
+	u16	hdr_len;		// sizeof nc_header (LE, all)
+	u16	packet_len;		// payload size (including ethhdr)
+	u16	packet_id;		// detects dropped packets
+#define MIN_HEADER	6
+
+	// all else is optional, and must start with:
+	// u16	vendorId;		// from usb-if
+	// u16	productId;
+} __attribute__((__packed__));
+
+#define	PAD_BYTE	((unsigned char)0xAC)
+
+struct nc_trailer {
+	u16	packet_id;
+} __attribute__((__packed__));
+
+// packets may use FLAG_FRAMING_NC and optional pad
+#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \
+				+ sizeof (struct ethhdr) \
+				+ (mtu) \
+				+ 1 \
+				+ sizeof (struct nc_trailer))
+
+#define MIN_FRAMED	FRAMED_SIZE(0)
+
+
+/*
  * Zero means no timeout; else, how long a 64 byte bulk packet may be queued
  * before the hardware drops it.  If that's done, the driver will need to
  * frame network packets to guard against the dropped USB packets.  The win32
@@ -652,285 +979,196 @@
 	return 0;
 }
 
-static const struct driver_info	net1080_info = {
-	description:	"NetChip TurboCONNECT",
-	flags:		FLAG_FRAMING_NC,
-	reset:		net1080_reset,
-	check_connect:	net1080_check_connect,
-
-	in: 1, out: 1,		// direction distinguishes these
-	epsize:	64,
-};
-
-#endif /* CONFIG_USB_NET1080 */
-
-
-
-#ifdef CONFIG_USB_PL2301
-
-/*-------------------------------------------------------------------------
- *
- * Prolific PL-2301/PL-2302 driver ... http://www.prolifictech.com
- *
- *-------------------------------------------------------------------------*/
-
-/*
- * Bits 0-4 can be used for software handshaking; they're set from
- * one end, cleared from the other, "read" with the interrupt byte.
- */
-#define	PL_S_EN		(1<<7)		/* (feature only) suspend enable */
-/* reserved bit -- rx ready (6) ? */
-#define	PL_TX_READY	(1<<5)		/* (interrupt only) transmit ready */
-#define	PL_RESET_OUT	(1<<4)		/* reset output pipe */
-#define	PL_RESET_IN	(1<<3)		/* reset input pipe */
-#define	PL_TX_C		(1<<2)		/* transmission complete */
-#define	PL_TX_REQ	(1<<1)		/* transmission received */
-#define	PL_PEER_E	(1<<0)		/* peer exists */
-
-static inline int
-pl_vendor_req (struct usbnet *dev, u8 req, u8 val, u8 index)
-{
-	return usb_control_msg (dev->udev,
-		usb_rcvctrlpipe (dev->udev, 0),
-		req,
-		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-		val, index,
-		0, 0,
-		CONTROL_TIMEOUT_JIFFIES);
-}
-
-static inline int
-pl_clear_QuickLink_features (struct usbnet *dev, int val)
-{
-	return pl_vendor_req (dev, 1, (u8) val, 0);
-}
-
-static inline int
-pl_set_QuickLink_features (struct usbnet *dev, int val)
+static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
 {
-	return pl_vendor_req (dev, 3, (u8) val, 0);
-}
+	struct nc_header	*header;
+	struct nc_trailer	*trailer;
 
-/*-------------------------------------------------------------------------*/
+	if (!(skb->len & 0x01)
+			|| MIN_FRAMED > skb->len
+			|| skb->len > FRAMED_SIZE (dev->net.mtu)) {
+		dev->stats.rx_frame_errors++;
+		dbg ("rx framesize %d range %d..%d mtu %d", skb->len,
+			MIN_FRAMED, FRAMED_SIZE (dev->net.mtu),
+			dev->net.mtu
+			);
+		return 0;
+	}
 
-static int pl_reset (struct usbnet *dev)
-{
-	return pl_set_QuickLink_features (dev,
-		PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);
-}
+	header = (struct nc_header *) skb->data;
+	le16_to_cpus (&header->hdr_len);
+	le16_to_cpus (&header->packet_len);
+	if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) {
+		dev->stats.rx_frame_errors++;
+		dbg ("packet too big, %d", header->packet_len);
+		return 0;
+	} else if (header->hdr_len < MIN_HEADER) {
+		dev->stats.rx_frame_errors++;
+		dbg ("header too short, %d", header->hdr_len);
+		return 0;
+	} else if (header->hdr_len > MIN_HEADER) {
+		// out of band data for us?
+		dbg ("header OOB, %d bytes",
+			header->hdr_len - MIN_HEADER);
+		// switch (vendor/product ids) { ... }
+	}
+	skb_pull (skb, header->hdr_len);
+
+	trailer = (struct nc_trailer *)
+		(skb->data + skb->len - sizeof *trailer);
+	skb_trim (skb, skb->len - sizeof *trailer);
 
-static int pl_check_connect (struct usbnet *dev)
-{
-	// FIXME test interrupt data PL_PEER_E bit
-	// plus, there's some handshake done by
-	// the prolific win32 driver... 
-	dbg ("%s: assuming peer is connected", dev->net.name);
-	return 0;
+	if ((header->packet_len & 0x01) == 0) {
+		if (skb->data [header->packet_len] != PAD_BYTE) {
+			dev->stats.rx_frame_errors++;
+			dbg ("bad pad");
+			return 0;
+		}
+		skb_trim (skb, skb->len - 1);
+	}
+	if (skb->len != header->packet_len) {
+		dev->stats.rx_frame_errors++;
+		dbg ("bad packet len %d (expected %d)",
+			skb->len, header->packet_len);
+		return 0;
+	}
+	if (header->packet_id != get_unaligned (&trailer->packet_id)) {
+		dev->stats.rx_fifo_errors++;
+		dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x",
+			header->packet_id, trailer->packet_id);
+		return 0;
+	}
+#if 0
+	devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len,
+		header->packet_len, header->packet_id);
+#endif
+	return 1;
 }
 
-static const struct driver_info	prolific_info = {
-	description:	"Prolific PL-2301/PL-2302",
-	reset:		pl_reset,
-	check_connect:	pl_check_connect,
-
-	in: 3, out: 2,
-	epsize:	64,
-};
-
-#endif /* CONFIG_USB_PL2301 */
-
-
-
-#ifdef CONFIG_USB_GENELINK
-
-/*-------------------------------------------------------------------------
- *
- * GeneSys GL620USB-A (www.genesyslogic.com.tw)
- *
- *-------------------------------------------------------------------------*/
-
-// control msg write command
-#define GENELINK_CONNECT_WRITE			0xF0
-// interrupt pipe index
-#define GENELINK_INTERRUPT_PIPE			0x03
-// interrupt read buffer size
-#define INTERRUPT_BUFSIZE				0x08
-// interrupt pipe interval value
-#define GENELINK_INTERRUPT_INTERVAL		0x10
-// max transmit packet number per transmit
-#define GL_MAX_TRANSMIT_PACKETS			32
-// max packet length
-#define GL_MAX_PACKET_LEN				1514
-// max receive buffer size 
-#define GL_RCV_BUF_SIZE					(((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4)
-
-struct gl_priv
-{ 
-	struct urb *irq_urb;
-	char irq_buf[INTERRUPT_BUFSIZE];
-};
-
-struct gl_packet 
-{
-	u32 packet_length;
-	char packet_data[1];
-};
-
-struct gl_header 
+static struct sk_buff *
+net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
 {
-	u32 packet_count;
-
-	struct gl_packet packets;
-};
-
-static inline int gl_control_write(struct usbnet *dev, u8 request, u16 value)
-{
-	int retval;
-
-	retval = usb_control_msg (dev->udev,
-							  usb_sndctrlpipe (dev->udev, 0),
-							  request,
-							  USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-							  value, 
-							  0,			// index
-							  0,			// data buffer
-							  0,			// size
-							  CONTROL_TIMEOUT_JIFFIES);
-
-	return retval;
-}
+	int			padlen;
+	struct sk_buff		*skb2;
 
-static void gl_interrupt_complete (struct urb *urb)
-{
-	int status = urb->status;
-	
-	if (status)
-		dbg("gl_interrupt_complete fail - %X\n", status);
-	else
-		dbg("gl_interrupt_complete success...\n");
-}
+	padlen = ((skb->len + sizeof (struct nc_header)
+			+ sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
+	if (!skb_cloned (skb)) {
+		int	headroom = skb_headroom (skb);
+		int	tailroom = skb_tailroom (skb);
 
-static inline int gl_interrupt_read(struct usbnet *dev)
-{
-	struct gl_priv *priv = dev->priv_data;
-	int	retval;
+		if ((padlen + sizeof (struct nc_trailer)) <= tailroom
+			    && sizeof (struct nc_header) <= headroom)
+			return skb;
 
-	// issue usb interrupt read
-	if (priv && priv->irq_urb) {
-		// submit urb
-		if ((retval = usb_submit_urb (priv->irq_urb)) != 0)
-			dbg("gl_interrupt_read: submit interrupt read urb fail - %X...\n", retval);
-		else
-			dbg("gl_interrupt_read: submit interrupt read urb success...\n");
+		if ((sizeof (struct nc_header) + padlen
+					+ sizeof (struct nc_trailer)) <
+				(headroom + tailroom)) {
+			skb->data = memmove (skb->head
+						+ sizeof (struct nc_header),
+					    skb->data, skb->len);
+			skb->tail = skb->data + skb->len;
+			return skb;
+		}
 	}
-
-	return 0;
+	skb2 = skb_copy_expand (skb,
+				sizeof (struct nc_header),
+				sizeof (struct nc_trailer) + padlen,
+				flags);
+	dev_kfree_skb_any (skb);
+	return skb2;
 }
 
-// check whether another side is connected
-static int genelink_check_connect (struct usbnet *dev)
-{
-	dbg ("%s: assuming peer is connected", dev->net.name);
-	return 0;
-
-	/*
-	// FIXME Uncomment this code after genelink_check_connect
-	// control hanshaking will be implemented
+static const struct driver_info	net1080_info = {
+	description:	"NetChip TurboCONNECT",
+	flags:		FLAG_FRAMING_NC,
+	reset:		net1080_reset,
+	check_connect:	net1080_check_connect,
+	rx_fixup:	net1080_rx_fixup,
+	tx_fixup:	net1080_tx_fixup,
 
-	int			retval;
+	in: 1, out: 1,		// direction distinguishes these
+	epsize:	64,
+};
 
-	dbg("genelink_check_connect...\n");
+#endif /* CONFIG_USB_NET1080 */
 
-	// issue a usb control write command to detect whether another side is connected
-	if ((retval = gl_control_write(dev, GENELINK_CONNECT_WRITE, 0)) != 0) {
-		dbg ("%s: genelink_check_connect control write fail - %X\n", dev->net.name, retval);
-		return retval;
-	} else {
-		dbg("%s: genelink_check_conntect control write success\n",dev->net.name);
 
-		// issue a usb interrupt read command to ack another side 
+
+#ifdef CONFIG_USB_PL2301
 
-		if ((retval = gl_interrupt_read(dev)) != 0) {
-			dbg("%s: genelink_check_connect interrupt read fail - %X\n", dev->net.name, retval);
-			return retval;
-		} else {
-			dbg("%s: genelink_check_connect interrupt read success\n", dev->net.name);
-		}
+/*-------------------------------------------------------------------------
+ *
+ * Prolific PL-2301/PL-2302 driver ... http://www.prolifictech.com
+ *
+ *-------------------------------------------------------------------------*/
 
-	}
-	*/
+/*
+ * Bits 0-4 can be used for software handshaking; they're set from
+ * one end, cleared from the other, "read" with the interrupt byte.
+ */
+#define	PL_S_EN		(1<<7)		/* (feature only) suspend enable */
+/* reserved bit -- rx ready (6) ? */
+#define	PL_TX_READY	(1<<5)		/* (interrupt only) transmit ready */
+#define	PL_RESET_OUT	(1<<4)		/* reset output pipe */
+#define	PL_RESET_IN	(1<<3)		/* reset input pipe */
+#define	PL_TX_C		(1<<2)		/* transmission complete */
+#define	PL_TX_REQ	(1<<1)		/* transmission received */
+#define	PL_PEER_E	(1<<0)		/* peer exists */
 
-	return 0;
+static inline int
+pl_vendor_req (struct usbnet *dev, u8 req, u8 val, u8 index)
+{
+	return usb_control_msg (dev->udev,
+		usb_rcvctrlpipe (dev->udev, 0),
+		req,
+		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		val, index,
+		0, 0,
+		CONTROL_TIMEOUT_JIFFIES);
 }
 
-// allocate and initialize the private data for genelink
-static int genelink_init_priv(struct usbnet *dev)
+static inline int
+pl_clear_QuickLink_features (struct usbnet *dev, int val)
 {
-	struct gl_priv *priv;
-
-	// allocate the private data structure
-	if ((priv = kmalloc (sizeof *priv, GFP_KERNEL)) == 0) {
-		dbg("%s: cannot allocate private data per device", dev->net.name);
-		return -ENOMEM;
-	}
-
-	// allocate irq urb
-	if ((priv->irq_urb = usb_alloc_urb(0)) == 0) {
-		dbg("%s: cannot allocate private irq urb per device", dev->net.name);
-		kfree(priv);
-		return -ENOMEM;
-	}
-
-	// fill irq urb
-	FILL_INT_URB(priv->irq_urb, dev->udev, usb_rcvintpipe(dev->udev, GENELINK_INTERRUPT_PIPE),
-				 priv->irq_buf, INTERRUPT_BUFSIZE, gl_interrupt_complete, 0, GENELINK_INTERRUPT_INTERVAL);
-
-	// set private data pointer
-	dev->priv_data = priv;
-
-	return 0;
+	return pl_vendor_req (dev, 1, (u8) val, 0);
 }
 
-// release the private data
-static int genelink_release_priv(struct usbnet *dev)
+static inline int
+pl_set_QuickLink_features (struct usbnet *dev, int val)
 {
-	struct gl_priv *priv = dev->priv_data;
-
-	if (!priv) 
-		return 0;
-	
-	// cancel irq urb first
-	usb_unlink_urb(priv->irq_urb);
-
-	// free irq urb
-	usb_free_urb(priv->irq_urb);
+	return pl_vendor_req (dev, 3, (u8) val, 0);
+}
 
-	// free the private data structure
-	kfree(priv);
+/*-------------------------------------------------------------------------*/
 
-	return 0;
+static int pl_reset (struct usbnet *dev)
+{
+	return pl_set_QuickLink_features (dev,
+		PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);
 }
 
-// reset the device status
-static int genelink_reset (struct usbnet *dev)
+static int pl_check_connect (struct usbnet *dev)
 {
-	// we don't need to reset, just return 0
+	// FIXME test interrupt data PL_PEER_E bit
+	// plus, there's some handshake done by
+	// the prolific win32 driver... 
+	dbg ("%s: assuming peer is connected", dev->net.name);
 	return 0;
 }
 
-static const struct driver_info	genelink_info = {
-	description:	"Genesys GeneLink",
-	flags:		FLAG_GENELINK | FLAG_NO_SETINT,
-	reset:		genelink_reset,
-	check_connect:	genelink_check_connect,
-	initialize_private: genelink_init_priv,
-	release_private: genelink_release_priv,
+static const struct driver_info	prolific_info = {
+	description:	"Prolific PL-2301/PL-2302",
+	flags:		FLAG_NO_SETINT,
+		/* some PL-2302 versions seem to fail usb_set_interface() */
+	reset:		pl_reset,
+	check_connect:	pl_check_connect,
 
-	in: 1, out: 2,		// direction distinguishes these
+	in: 3, out: 2,
 	epsize:	64,
 };
 
-#endif /* CONFIG_USB_GENELINK */
+#endif /* CONFIG_USB_PL2301 */
 
 
 
@@ -946,11 +1184,19 @@
 
 	if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)
 		return -EINVAL;
+#ifdef	CONFIG_USB_NET1080
 	if (((dev->driver_info->flags) & FLAG_FRAMING_NC)) {
 		if (FRAMED_SIZE (new_mtu) > MAX_PACKET)
 			return -EINVAL;
+	}
+#endif
+#ifdef	CONFIG_USB_GENESYS
+	if (((dev->driver_info->flags) & FLAG_FRAMING_GL)
+			&& new_mtu > GL_MAX_PACKET_LEN)
+		return -EINVAL;
+#endif
 	// no second zero-length packet read wanted after mtu-sized packets
-	} else if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0)
+	if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0)
 		return -EDOM;
 	net->mtu = new_mtu;
 	return 0;
@@ -994,14 +1240,16 @@
 	unsigned long		lockflags;
 	size_t			size;
 
-#ifdef CONFIG_USB_GENELINK
-	if (dev->driver_info->flags & FLAG_GENELINK)
-		size = GL_RCV_BUF_SIZE;
-	else
-#endif
+#ifdef CONFIG_USB_NET1080
 	if (dev->driver_info->flags & FLAG_FRAMING_NC)
 		size = FRAMED_SIZE (dev->net.mtu);
 	else
+#endif
+#ifdef CONFIG_USB_GENESYS
+	if (dev->driver_info->flags & FLAG_FRAMING_GL)
+		size = GL_RCV_BUF_SIZE;
+	else
+#endif
 		size = (sizeof (struct ethhdr) + dev->net.mtu);
 
 	if ((skb = alloc_skb (size, flags)) == 0) {
@@ -1055,181 +1303,14 @@
 
 static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
 {
-	if (dev->driver_info->flags & FLAG_FRAMING_NC) {
-		struct nc_header	*header;
-		struct nc_trailer	*trailer;
-
-		if (!(skb->len & 0x01)
-				|| MIN_FRAMED > skb->len
-				|| skb->len > FRAMED_SIZE (dev->net.mtu)) {
-			dev->stats.rx_frame_errors++;
-			dbg ("rx framesize %d range %d..%d mtu %d", skb->len,
-				MIN_FRAMED, FRAMED_SIZE (dev->net.mtu),
-				dev->net.mtu
-				);
-			goto error;
-		}
-
-		header = (struct nc_header *) skb->data;
-		le16_to_cpus (&header->hdr_len);
-		le16_to_cpus (&header->packet_len);
-		if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) {
-			dev->stats.rx_frame_errors++;
-			dbg ("packet too big, %d", header->packet_len);
-			goto error;
-		} else if (header->hdr_len < MIN_HEADER) {
-			dev->stats.rx_frame_errors++;
-			dbg ("header too short, %d", header->hdr_len);
-			goto error;
-		} else if (header->hdr_len > MIN_HEADER) {
-			// out of band data for us?
-			dbg ("header OOB, %d bytes",
-				header->hdr_len - MIN_HEADER);
-			// switch (vendor/product ids) { ... }
-		}
-		skb_pull (skb, header->hdr_len);
-
-		trailer = (struct nc_trailer *)
-			(skb->data + skb->len - sizeof *trailer);
-		skb_trim (skb, skb->len - sizeof *trailer);
-
-		if ((header->packet_len & 0x01) == 0) {
-			if (skb->data [header->packet_len] != PAD_BYTE) {
-				dev->stats.rx_frame_errors++;
-				dbg ("bad pad");
-				goto error;
-			}
-			skb_trim (skb, skb->len - 1);
-		}
-		if (skb->len != header->packet_len) {
-			dev->stats.rx_frame_errors++;
-			dbg ("bad packet len %d (expected %d)",
-				skb->len, header->packet_len);
-			goto error;
-		}
-		if (header->packet_id != get_unaligned (&trailer->packet_id)) {
-			dev->stats.rx_fifo_errors++;
-			dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x",
-				header->packet_id, trailer->packet_id);
-			goto error;
-		}
-#if 0
-		devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len,
-			header->packet_len, header->packet_id);
-#endif
-	} else {
-		// we trust the network stack to remove
-		// the extra byte we may have appended
-	}
-
-#ifdef CONFIG_USB_GENELINK
-	if (dev->driver_info->flags & FLAG_GENELINK) {
-		struct gl_header *header;
-		struct gl_packet *current_packet;
-		struct sk_buff *gl_skb;
-		int status;
-		u32 size;
-
-		header = (struct gl_header *)skb->data;
-
-		// get the packet count of the received skb
-		le32_to_cpus(&header->packet_count);
-
-//		dbg("receive packet count = %d", header->packet_count);
-
-		if ((header->packet_count > GL_MAX_TRANSMIT_PACKETS) || 
-		    (header->packet_count < 0)) {
-			dbg("genelink: illegal received packet count %d", header->packet_count);
-			goto error;
-		}
-
-		// set the current packet pointer to the first packet
-		current_packet = &(header->packets);
-
-		// decrement the length for the packet count size 4 bytes
-		skb_pull(skb, 4);
-
-		while (header->packet_count > 1) {
-			// get the packet length
-			size = current_packet->packet_length;
-
-			// this may be a broken packet
-			if (size > GL_MAX_PACKET_LEN) {
-				dbg("genelink: illegal received packet length %d, maybe a broken packet", size);
-				goto error;
-			}
-
-			// allocate the skb for the individual packet
-			gl_skb = alloc_skb (size, in_interrupt () ? GFP_ATOMIC : GFP_KERNEL);
-
-			if (gl_skb == 0)
-				goto error;
-
-			// copy the packet data to the new skb
-			memcpy(gl_skb->data,current_packet->packet_data,size);
-
-			// set skb data size
-			gl_skb->len = size;
-/*
-			dbg("rx_process one gl_packet, size = %d...", size);
-
-			dbg("%02X %02X %02X %02X %02X %02X",
-				(u8)gl_skb->data[0],(u8)gl_skb->data[1],(u8)gl_skb->data[2],
-				(u8)gl_skb->data[3],(u8)gl_skb->data[4],(u8)gl_skb->data[5]);
-			dbg("%02X %02X %02X %02X %02X %02X\n",
-				(u8)gl_skb->data[6],(u8)gl_skb->data[7],(u8)gl_skb->data[8],
-				(u8)gl_skb->data[9],(u8)gl_skb->data[10],(u8)gl_skb->data[11]);
-*/
-			gl_skb->dev = &dev->net;
-
-			// determine the packet's protocol ID
-			gl_skb->protocol = eth_type_trans(gl_skb, &dev->net);
-
-			// update the status
-			dev->stats.rx_packets++;
-			dev->stats.rx_bytes += size;
-
-			// notify os of the received packet
-			status = netif_rx (gl_skb);
-
-//			dev_kfree_skb (gl_skb);	// just for debug purpose, delete this line for normal operation
-
-			// advance to the next packet
-			current_packet = (struct gl_packet *)(current_packet->packet_data + size);
-
-			header->packet_count --;
-
-			// shift the data pointer to the next gl_packet
-			skb_pull(skb, size + 4);
-		}	// while (header->packet_count > 1)
-
-		// skip the packet length field 4 bytes
-		skb_pull(skb, 4);
-	}
-#endif
+	if (dev->driver_info->rx_fixup
+			&& !dev->driver_info->rx_fixup (dev, skb))
+		goto error;
+	// else network stack removes extra byte if we forced a short packet
 
 	if (skb->len) {
 		int	status;
 
-#ifdef CONFIG_USB_GENELINK
-/*
-		dbg("rx_process one packet, size = %d", skb->len);
-
-		dbg("%02X %02X %02X %02X %02X %02X",
-			(u8)skb->data[0],(u8)skb->data[1],(u8)skb->data[2],
-			(u8)skb->data[3],(u8)skb->data[4],(u8)skb->data[5]);
-		dbg("%02X %02X %02X %02X %02X %02X\n",
-			(u8)skb->data[6],(u8)skb->data[7],(u8)skb->data[8],
-			(u8)skb->data[9],(u8)skb->data[10],(u8)skb->data[11]);
-*/
-
-		if ((dev->driver_info->flags & FLAG_GENELINK) &&
-		    (skb->len > GL_MAX_PACKET_LEN)) {
-			dbg("genelink: illegal received packet length %d, maybe a broken packet", skb->len);
-			goto error;
-		}
-#endif
-
 // FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ?
 
 		skb->dev = &dev->net;
@@ -1241,7 +1322,7 @@
 		devdbg (dev, "< rx, len %d, type 0x%x",
 			skb->len + sizeof (struct ethhdr), skb->protocol);
 #endif
-		memset (skb->cb,0,sizeof(struct skb_data));
+		memset (skb->cb, 0, sizeof (struct skb_data));
 		status = netif_rx (skb);
 		if (status != NET_RX_SUCCESS)
 			devdbg (dev, "netif_rx status %d", status);
@@ -1359,7 +1440,7 @@
 	DECLARE_WAITQUEUE (wait, current);
 
 	mutex_lock (&dev->mutex);
-	netif_stop_queue(net);
+	netif_stop_queue (net);
 
 	devdbg (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld",
 		dev->stats.rx_packets, dev->stats.tx_packets, 
@@ -1375,16 +1456,13 @@
 	while (skb_queue_len (&dev->rxq)
 			&& skb_queue_len (&dev->txq)
 			&& skb_queue_len (&dev->done)) {
-		set_current_state(TASK_UNINTERRUPTIBLE);
+		set_current_state (TASK_UNINTERRUPTIBLE);
 		schedule_timeout (UNLINK_TIMEOUT_JIFFIES);
 		dbg ("waited for %d urb completions", temp);
 	}
 	dev->wait = 0;
 	remove_wait_queue (&unlink_wakeup, &wait); 
 
-	if (dev->driver_info->release_private)
-		dev->driver_info->release_private(dev);
-
 	mutex_unlock (&dev->mutex);
 	return 0;
 }
@@ -1412,14 +1490,6 @@
 		goto done;
 	}
 
-	// initialize the private resources
-	if (info->initialize_private) {
-		if ((retval = info->initialize_private(dev)) < 0) {
-			dbg("%s: open initialize private fail", dev->net.name);
-			goto done;
-		}
-	}
-
 	// insist peer be connected
 	if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
 		devdbg (dev, "can't open; %d", retval);
@@ -1429,8 +1499,10 @@
 	netif_start_queue (net);
 	devdbg (dev, "open: enable queueing (rx %d, tx %d) mtu %d %s framing",
 		RX_QLEN, TX_QLEN, dev->net.mtu,
-		(info->flags & FLAG_FRAMING_NC)
-		    ? "NetChip"
+		(info->flags & (FLAG_FRAMING_NC | FLAG_FRAMING_GL))
+		    ? ((info->flags & FLAG_FRAMING_NC)
+			? "NetChip"
+			: "GeneSys")
 		    : "raw"
 		);
 
@@ -1446,7 +1518,7 @@
 /* usb_clear_halt cannot be called in interrupt context */
 
 static void
-tx_clear_halt(void *data)
+tx_clear_halt (void *data)
 {
 	struct usbnet		*dev = data;
 
@@ -1467,7 +1539,7 @@
 		if (dev->ctrl_task.sync == 0) {
 			dev->ctrl_task.routine = tx_clear_halt;
 			dev->ctrl_task.data = dev;
-			schedule_task(&dev->ctrl_task);
+			schedule_task (&dev->ctrl_task);
 		} else {
 			dbg ("Cannot clear TX stall");
 		}
@@ -1491,79 +1563,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-static inline struct sk_buff *fixup_skb (struct sk_buff *skb, int flags)
-{
-	int			padlen;
-	struct sk_buff		*skb2;
-
-	padlen = ((skb->len + sizeof (struct nc_header)
-			+ sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
-	if (!skb_cloned (skb)) {
-		int	headroom = skb_headroom (skb);
-		int	tailroom = skb_tailroom (skb);
-
-		if ((padlen + sizeof (struct nc_trailer)) <= tailroom
-			    && sizeof (struct nc_header) <= headroom)
-			return skb;
-
-		if ((sizeof (struct nc_header) + padlen
-					+ sizeof (struct nc_trailer)) <
-				(headroom + tailroom)) {
-			skb->data = memmove (skb->head
-						+ sizeof (struct nc_header),
-					    skb->data, skb->len);
-			skb->tail = skb->data + skb->len;
-			return skb;
-		}
-	}
-	skb2 = skb_copy_expand (skb,
-				sizeof (struct nc_header),
-				sizeof (struct nc_trailer) + padlen,
-				flags);
-	dev_kfree_skb_any (skb);
-	return skb2;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef CONFIG_USB_GENELINK
-static struct sk_buff *gl_build_skb (struct sk_buff *skb)
-{
-	struct sk_buff *skb2;
-	int padlen;
-
-	int	headroom = skb_headroom (skb);
-	int	tailroom = skb_tailroom (skb);
-
-//	dbg("headroom = %d, tailroom = %d", headroom, tailroom);
-
-	padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1;
-
-	if ((!skb_cloned (skb)) && ((headroom + tailroom) >= (padlen + (4 + 4*1)))) {
-		if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
-			skb->data = memmove (skb->head + (4 + 4*1),
-					     skb->data, skb->len);
-			skb->tail = skb->data + skb->len;
-		}
-		skb2 = skb;
-	} else {
-		skb2 = skb_copy_expand (skb, (4 + 4*1) , padlen, in_interrupt () ? GFP_ATOMIC : GFP_KERNEL);
-
-		if (!skb2) {
-			dbg("genelink: skb_copy_expand fail");
-			return 0;
-		}
-
-		// free the original skb
-		dev_kfree_skb_any (skb);
-	}
-
-	return skb2;
-}
-#endif
-
-/*-------------------------------------------------------------------------*/
-
 static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
 {
 	struct usbnet		*dev = (struct usbnet *) net->priv;
@@ -1571,29 +1570,24 @@
 	int			retval = NET_XMIT_SUCCESS;
 	struct urb		*urb = 0;
 	struct skb_data		*entry;
-	struct nc_header	*header = 0;
-	struct nc_trailer	*trailer = 0;
 	struct driver_info	*info = dev->driver_info;
 	int			flags;
+#ifdef	CONFIG_USB_NET1080
+	struct nc_header	*header = 0;
+	struct nc_trailer	*trailer = 0;
+#endif	/* CONFIG_USB_NET1080 */
 
 	flags = in_interrupt () ? GFP_ATOMIC : GFP_KERNEL;
 
-	if (info->flags & FLAG_FRAMING_NC) {
-		struct sk_buff	*skb2;
-		skb2 = fixup_skb (skb, flags);
-		if (!skb2) {
-			dbg ("can't fixup skb");
+	// some devices want funky USB-level framing, for
+	// win32 driver (usually) and/or hardware quirks
+	if (info->tx_fixup) {
+		skb = info->tx_fixup (dev, skb, flags);
+		if (!skb) {
+			dbg ("can't tx_fixup skb");
 			goto drop;
 		}
-		skb = skb2;
-	}
-
-#ifdef CONFIG_USB_GENELINK
-	if ((info->flags & FLAG_GENELINK) && (skb = gl_build_skb(skb)) == 0) {
-		dbg("can't build skb for genelink transmit");
-		goto drop;
 	}
-#endif
 
 	if (!(urb = usb_alloc_urb (0))) {
 		dbg ("no urb");
@@ -1606,6 +1600,10 @@
 	entry->state = tx_start;
 	entry->length = length;
 
+	// FIXME: reorganize a bit, so that fixup() fills out NetChip
+	// framing too. (Packet ID update needs the spinlock...)
+
+#ifdef	CONFIG_USB_NET1080
 	if (info->flags & FLAG_FRAMING_NC) {
 		header = (struct nc_header *) skb_push (skb, sizeof *header);
 		header->hdr_len = cpu_to_le16 (sizeof (*header));
@@ -1613,40 +1611,12 @@
 		if (!((skb->len + sizeof *trailer) & 0x01))
 			*skb_put (skb, 1) = PAD_BYTE;
 		trailer = (struct nc_trailer *) skb_put (skb, sizeof *trailer);
-	}
-#ifdef CONFIG_USB_GENELINK
-	  else if (info->flags & FLAG_GENELINK) {
-		u32 *packet_count, *packet_len;
-
-		// attach the packet count to the header
-		packet_count = (u32 *)skb_push(skb, (4 + 4*1));
-		packet_len = packet_count + 1;
-
-		// set packet to 1
-		*packet_count = 1;
-
-		// set packet length
-		*packet_len = length;
-
-		// add padding byte
-		if ((skb->len % EP_SIZE(dev)) == 0)
-			skb_put(skb, 1);
-	}
-#endif
-	  else if ((length % EP_SIZE (dev)) == 0) {
-		// not all hardware behaves with USB_ZERO_PACKET,
-		// so we add an extra one-byte packet
-		if (skb_shared (skb)) {
-			struct sk_buff *skb2;
-			skb2 = skb_unshare (skb, flags);
-			if (!skb2) {
-				dbg ("can't unshare skb");
-				goto drop;
-			}
-			skb = skb2;
-		}
+	} else
+#endif	/* CONFIG_USB_NET1080 */
+
+	/* don't assume the hardware handles USB_ZERO_PACKET */
+	if ((length % EP_SIZE (dev)) == 0)
 		skb->len++;
-	}
 
 	FILL_BULK_URB (urb, dev->udev,
 			usb_sndbulkpipe (dev->udev, info->out),
@@ -1658,6 +1628,8 @@
 	// FIXME urb->timeout = ... jiffies ... ;
 
 	spin_lock_irqsave (&dev->txq.lock, flags);
+
+#ifdef	CONFIG_USB_NET1080
 	if (info->flags & FLAG_FRAMING_NC) {
 		header->packet_id = cpu_to_le16 (dev->packet_id++);
 		put_unaligned (header->packet_id, &trailer->packet_id);
@@ -1667,6 +1639,7 @@
 			header->packet_id);
 #endif
 	}
+#endif	/* CONFIG_USB_NET1080 */
 
 	netif_stop_queue (net);
 	if ((retval = usb_submit_urb (urb)) != 0) {
@@ -1685,7 +1658,8 @@
 drop:
 		retval = NET_XMIT_DROP;
 		dev->stats.tx_dropped++;
-		dev_kfree_skb_any (skb);
+		if (skb)
+			dev_kfree_skb_any (skb);
 		usb_free_urb (urb);
 #ifdef	VERBOSE
 	} else {
@@ -1911,6 +1885,13 @@
 },
 #endif
 
+#ifdef	CONFIG_USB_GENESYS
+{
+	USB_DEVICE (0x05e3, 0x0502),	// GL620USB-A
+	driver_info:	(unsigned long) &genelink_info,
+},
+#endif
+
 #ifdef	CONFIG_USB_LINUXDEV
 /*
  * for example, this can be a host side talk-to-PDA driver.
@@ -1944,12 +1925,7 @@
 },
 #endif
 
-#ifdef	CONFIG_USB_GENELINK
-{
-	USB_DEVICE (0x05e3, 0x0502),	// GL620USB-A
-	driver_info:	(unsigned long) &genelink_info,
-},
-#endif
+/* KC2190 from www.sepoong.co.kr "InstaNET" */
 
 	{ },		// END
 };
diff -urN linux-2.4.15-pre8/fs/block_dev.c linux/fs/block_dev.c
--- linux-2.4.15-pre8/fs/block_dev.c	Thu Oct 25 13:58:35 2001
+++ linux/fs/block_dev.c	Mon Dec  3 23:32:46 2001
@@ -113,6 +113,11 @@
 	return 0;
 }
 
+static int blkdev_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+{
+	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, blkdev_get_block);
+}
+
 static int blkdev_writepage(struct page * page)
 {
 	return block_write_full_page(page, blkdev_get_block);
@@ -640,6 +645,7 @@
 	sync_page: block_sync_page,
 	prepare_write: blkdev_prepare_write,
 	commit_write: blkdev_commit_write,
+	direct_IO: blkdev_direct_IO,
 };
 
 struct file_operations def_blk_fops = {
diff -urN linux-2.4.15-pre8/fs/buffer.c linux/fs/buffer.c
--- linux-2.4.15-pre8/fs/buffer.c	Mon Dec  3 23:32:36 2001
+++ linux/fs/buffer.c	Mon Dec  3 23:32:46 2001
@@ -1172,6 +1172,7 @@
 	struct buffer_head * bh;
 
 	bh = getblk(dev, block, size);
+	touch_buffer(bh);
 	if (buffer_uptodate(bh))
 		return bh;
 	ll_rw_block(READ, 1, &bh);
@@ -1997,6 +1998,47 @@
 	return tmp.b_blocknr;
 }
 
+int generic_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize, get_block_t * get_block)
+{
+	int i, nr_blocks, retval;
+	unsigned long * blocks = iobuf->blocks;
+
+	nr_blocks = iobuf->length / blocksize;
+	/* build the blocklist */
+	for (i = 0; i < nr_blocks; i++, blocknr++) {
+		struct buffer_head bh;
+
+		bh.b_state = 0;
+		bh.b_dev = inode->i_dev;
+		bh.b_size = blocksize;
+
+		retval = get_block(inode, blocknr, &bh, rw == READ ? 0 : 1);
+		if (retval)
+			goto out;
+
+		if (rw == READ) {
+			if (buffer_new(&bh))
+				BUG();
+			if (!buffer_mapped(&bh)) {
+				/* there was an hole in the filesystem */
+				blocks[i] = -1UL;
+				continue;
+			}
+		} else {
+			if (buffer_new(&bh))
+				unmap_underlying_metadata(&bh);
+			if (!buffer_mapped(&bh))
+				BUG();
+		}
+		blocks[i] = bh.b_blocknr;
+	}
+
+	retval = brw_kiovec(rw, 1, &iobuf, inode->i_dev, iobuf->blocks, blocksize);
+
+ out:
+	return retval;
+}
+
 /*
  * IO completion routine for a buffer_head being used for kiobuf IO: we
  * can't dispatch the kiobuf callback until io_count reaches 0.  
@@ -2350,10 +2392,13 @@
 	unsigned long index;
 	int sizebits;
 
-	if ((size & 511) || (size > PAGE_SIZE)) {
-		printk(KERN_ERR "VFS: grow_buffers: size = %d\n",size);
-		return 0;
-	}
+	/* Size must be multiple of hard sectorsize */
+	if (size & (get_hardsect_size(dev)-1))
+		BUG();
+	/* Size must be within 512 bytes and PAGE_SIZE */
+	if (size < 512 || size > PAGE_SIZE)
+		BUG();
+
 	sizebits = -1;
 	do {
 		sizebits++;
diff -urN linux-2.4.15-pre8/fs/ext2/inode.c linux/fs/ext2/inode.c
--- linux-2.4.15-pre8/fs/ext2/inode.c	Thu Oct 11 09:43:38 2001
+++ linux/fs/ext2/inode.c	Mon Dec  3 23:32:46 2001
@@ -592,13 +592,18 @@
 {
 	return generic_block_bmap(mapping,block,ext2_get_block);
 }
+static int ext2_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+{
+	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, ext2_get_block);
+}
 struct address_space_operations ext2_aops = {
 	readpage: ext2_readpage,
 	writepage: ext2_writepage,
 	sync_page: block_sync_page,
 	prepare_write: ext2_prepare_write,
 	commit_write: generic_commit_write,
-	bmap: ext2_bmap
+	bmap: ext2_bmap,
+	direct_IO: ext2_direct_IO,
 };
 
 /*
diff -urN linux-2.4.15-pre8/fs/inode.c linux/fs/inode.c
--- linux-2.4.15-pre8/fs/inode.c	Mon Dec  3 23:32:36 2001
+++ linux/fs/inode.c	Mon Dec  3 23:32:46 2001
@@ -571,8 +571,7 @@
 			continue;
 		invalidate_inode_buffers(inode);
 		if (!atomic_read(&inode->i_count)) {
-			list_del(&inode->i_hash);
-			INIT_LIST_HEAD(&inode->i_hash);
+			list_del_init(&inode->i_hash);
 			list_del(&inode->i_list);
 			list_add(&inode->i_list, dispose);
 			inode->i_state |= I_FREEING;
@@ -1029,13 +1028,14 @@
 void iput(struct inode *inode)
 {
 	if (inode) {
+		struct super_block *sb = inode->i_sb;
 		struct super_operations *op = NULL;
 
 		if (inode->i_state == I_CLEAR)
 			BUG();
 
-		if (inode->i_sb && inode->i_sb->s_op)
-			op = inode->i_sb->s_op;
+		if (sb && sb->s_op)
+			op = sb->s_op;
 		if (op && op->put_inode)
 			op->put_inode(inode);
 
@@ -1065,7 +1065,7 @@
 			if (inode->i_state != I_CLEAR)
 				BUG();
 		} else {
-			if (!list_empty(&inode->i_hash)) {
+			if (!list_empty(&inode->i_hash) && sb && sb->s_root) {
 				if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
 					list_del(&inode->i_list);
 					list_add(&inode->i_list, &inode_unused);
@@ -1074,9 +1074,8 @@
 				spin_unlock(&inode_lock);
 				return;
 			} else {
-				/* magic nfs path */
-				list_del(&inode->i_list);
-				INIT_LIST_HEAD(&inode->i_list);
+				list_del_init(&inode->i_list);
+				list_del_init(&inode->i_hash);
 				inode->i_state|=I_FREEING;
 				inodes_stat.nr_inodes--;
 				spin_unlock(&inode_lock);
diff -urN linux-2.4.15-pre8/fs/reiserfs/Makefile linux/fs/reiserfs/Makefile
--- linux-2.4.15-pre8/fs/reiserfs/Makefile	Mon Dec  3 23:32:36 2001
+++ linux/fs/reiserfs/Makefile	Mon Dec  3 23:32:46 2001
@@ -13,13 +13,13 @@
 
 obj-m   := $(O_TARGET)
 
-# gcc -O2 (the kernel default)  is overaggressive on ppc when many inline
+# gcc -O2 (the kernel default)  is overaggressive on ppc32 when many inline
 # functions are used.  This causes the compiler to advance the stack
 # pointer out of the available stack space, corrupting kernel space,
-# and causing a panic. Since this behavior only affects ppc, this ifeq
+# and causing a panic. Since this behavior only affects ppc32, this ifeq
 # will work around it. If any other architecture displays this behavior,
 # add it here.
-ifeq ($(shell uname -m),ppc) 
+ifeq ($(CONFIG_PPC32),y)
 EXTRA_CFLAGS := -O1
 endif
 
diff -urN linux-2.4.15-pre8/fs/super.c linux/fs/super.c
--- linux-2.4.15-pre8/fs/super.c	Tue Oct 30 15:08:12 2001
+++ linux/fs/super.c	Mon Dec  3 23:32:46 2001
@@ -739,6 +739,7 @@
 	dput(root);
 	fsync_super(sb);
 	lock_super(sb);
+	invalidate_inodes(sb);	/* bad name - it should be evict_inodes() */
 	if (sop) {
 		if (sop->write_super && sb->s_dirt)
 			sop->write_super(sb);
diff -urN linux-2.4.15-pre8/include/linux/blkdev.h linux/include/linux/blkdev.h
--- linux-2.4.15-pre8/include/linux/blkdev.h	Mon Nov  5 12:42:57 2001
+++ linux/include/linux/blkdev.h	Mon Dec  3 23:32:46 2001
@@ -195,11 +195,15 @@
 
 static inline int get_hardsect_size(kdev_t dev)
 {
-	extern int *hardsect_size[];
-	if (hardsect_size[MAJOR(dev)] != NULL)
-		return hardsect_size[MAJOR(dev)][MINOR(dev)];
-	else
-		return 512;
+	int retval = 512;
+	int major = MAJOR(dev);
+
+	if (hardsect_size[major]) {
+		int minor = MINOR(dev);
+		if (hardsect_size[major][minor])
+			retval = hardsect_size[major][minor];
+	}
+	return retval;
 }
 
 #define blk_finished_io(nsects)	do { } while (0)
diff -urN linux-2.4.15-pre8/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.4.15-pre8/include/linux/fs.h	Mon Dec  3 23:32:36 2001
+++ linux/include/linux/fs.h	Mon Dec  3 23:32:46 2001
@@ -1386,6 +1386,7 @@
 int generic_block_bmap(struct address_space *, long, get_block_t *);
 int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
+extern int generic_direct_IO(int, struct inode *, struct kiobuf *, unsigned long, int, get_block_t *);
 
 extern int waitfor_one_page(struct page*);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
diff -urN linux-2.4.15-pre8/include/linux/mm.h linux/include/linux/mm.h
--- linux-2.4.15-pre8/include/linux/mm.h	Mon Dec  3 23:32:36 2001
+++ linux/include/linux/mm.h	Mon Dec  3 23:32:46 2001
@@ -416,6 +416,7 @@
 extern int ptrace_attach(struct task_struct *tsk);
 extern int ptrace_detach(struct task_struct *, unsigned int);
 extern void ptrace_disable(struct task_struct *);
+extern int ptrace_check_attach(struct task_struct *task, int kill);
 
 /*
  * On a two-level page table, this ends up being trivial. Thus the
diff -urN linux-2.4.15-pre8/include/linux/sched.h linux/include/linux/sched.h
--- linux-2.4.15-pre8/include/linux/sched.h	Mon Dec  3 23:32:36 2001
+++ linux/include/linux/sched.h	Mon Dec  3 23:32:46 2001
@@ -304,8 +304,16 @@
 	long nice;
 	unsigned long policy;
 	struct mm_struct *mm;
-	int has_cpu, processor;
-	unsigned long cpus_allowed;
+	int processor;
+	/*
+	 * cpus_runnable is ~0 if the process is not running on any
+	 * CPU. It's (1 << cpu) if it's running on a CPU. This mask
+	 * is updated under the runqueue lock.
+	 *
+	 * To determine whether a process might run on a CPU, this
+	 * mask is AND-ed with cpus_allowed.
+	 */
+	unsigned long cpus_runnable, cpus_allowed;
 	/*
 	 * (only the 'next' pointer fits into the cacheline, but
 	 * that's just fine.)
@@ -464,6 +472,7 @@
     policy:		SCHED_OTHER,					\
     mm:			NULL,						\
     active_mm:		&init_mm,					\
+    cpus_runnable:	-1,						\
     cpus_allowed:	-1,						\
     run_list:		LIST_HEAD_INIT(tsk.run_list),			\
     next_task:		&tsk,						\
@@ -539,6 +548,19 @@
 		;
 
 	return p;
+}
+
+#define task_has_cpu(tsk) ((tsk)->cpus_runnable != ~0UL)
+
+static inline void task_set_cpu(struct task_struct *tsk, unsigned int cpu)
+{
+	tsk->processor = cpu;
+	tsk->cpus_runnable = 1UL << cpu;
+}
+
+static inline void task_release_cpu(struct task_struct *tsk)
+{
+	tsk->cpus_runnable = ~0UL;
 }
 
 /* per-UID process charging. */
diff -urN linux-2.4.15-pre8/kernel/exit.c linux/kernel/exit.c
--- linux-2.4.15-pre8/kernel/exit.c	Mon Dec  3 23:32:36 2001
+++ linux/kernel/exit.c	Mon Dec  3 23:32:46 2001
@@ -35,13 +35,13 @@
 		 */
 		for (;;) {
 			task_lock(p);
-			if (!p->has_cpu)
+			if (!task_has_cpu(p))
 				break;
 			task_unlock(p);
 			do {
 				cpu_relax();
 				barrier();
-			} while (p->has_cpu);
+			} while (task_has_cpu(p));
 		}
 		task_unlock(p);
 #endif
diff -urN linux-2.4.15-pre8/kernel/fork.c linux/kernel/fork.c
--- linux-2.4.15-pre8/kernel/fork.c	Tue Oct 23 17:44:15 2001
+++ linux/kernel/fork.c	Mon Dec  3 23:32:46 2001
@@ -638,7 +638,7 @@
 #ifdef CONFIG_SMP
 	{
 		int i;
-		p->has_cpu = 0;
+		p->cpus_runnable = ~0UL;
 		p->processor = current->processor;
 		/* ?? should we just memset this ?? */
 		for(i = 0; i < smp_num_cpus; i++)
diff -urN linux-2.4.15-pre8/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-2.4.15-pre8/kernel/ksyms.c	Mon Nov  5 13:40:59 2001
+++ linux/kernel/ksyms.c	Mon Dec  3 23:32:46 2001
@@ -199,6 +199,7 @@
 EXPORT_SYMBOL(unlock_buffer);
 EXPORT_SYMBOL(__wait_on_buffer);
 EXPORT_SYMBOL(___wait_on_page);
+EXPORT_SYMBOL(generic_direct_IO);
 EXPORT_SYMBOL(block_write_full_page);
 EXPORT_SYMBOL(block_read_full_page);
 EXPORT_SYMBOL(block_prepare_write);
diff -urN linux-2.4.15-pre8/kernel/ptrace.c linux/kernel/ptrace.c
--- linux-2.4.15-pre8/kernel/ptrace.c	Tue Sep 18 16:32:16 2001
+++ linux/kernel/ptrace.c	Mon Dec  3 23:32:46 2001
@@ -16,6 +16,42 @@
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
 
+/*
+ * Check that we have indeed attached to the thing..
+ */
+int ptrace_check_attach(struct task_struct *child, int kill)
+{
+	if (!(child->ptrace & PT_PTRACED))
+		return -ESRCH;
+
+	if (child->p_pptr != current)
+		return -ESRCH;
+
+	if (!kill) {
+		if (child->state != TASK_STOPPED)
+			return -ESRCH;
+#ifdef CONFIG_SMP
+		/* Make sure the child gets off its CPU.. */
+		for (;;) {
+			task_lock(child);
+			if (!task_has_cpu(child))
+				break;
+			task_unlock(child);
+			do {
+				if (child->state != TASK_STOPPED)
+					return -ESRCH;
+				barrier();
+				cpu_relax();
+			} while (task_has_cpu(child));
+		}
+		task_unlock(child);
+#endif		
+	}
+
+	/* All systems go.. */
+	return 0;
+}
+
 int ptrace_attach(struct task_struct *task)
 {
 	task_lock(task);
diff -urN linux-2.4.15-pre8/kernel/sched.c linux/kernel/sched.c
--- linux-2.4.15-pre8/kernel/sched.c	Mon Dec  3 23:32:36 2001
+++ linux/kernel/sched.c	Mon Dec  3 23:32:46 2001
@@ -28,6 +28,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/completion.h>
 #include <linux/prefetch.h>
+#include <linux/compiler.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -114,8 +115,8 @@
 #ifdef CONFIG_SMP
 
 #define idle_task(cpu) (init_tasks[cpu_number_map(cpu)])
-#define can_schedule(p,cpu) ((!(p)->has_cpu) && \
-				((p)->cpus_allowed & (1 << cpu)))
+#define can_schedule(p,cpu) \
+	((p)->cpus_runnable & (p)->cpus_allowed & (1 << cpu))
 
 #else
 
@@ -455,11 +456,11 @@
 
 	/*
 	 * prev->policy can be written from here only before `prev'
-	 * can be scheduled (before setting prev->has_cpu to zero).
+	 * can be scheduled (before setting prev->cpus_runnable to ~0UL).
 	 * Of course it must also be read before allowing prev
 	 * to be rescheduled, but since the write depends on the read
 	 * to complete, wmb() is enough. (the spin_lock() acquired
-	 * before setting has_cpu is not enough because the spin_lock()
+	 * before setting cpus_runnable is not enough because the spin_lock()
 	 * common code semantics allows code outside the critical section
 	 * to enter inside the critical section)
 	 */
@@ -468,12 +469,12 @@
 	wmb();
 
 	/*
-	 * fast path falls through. We have to clear has_cpu before
-	 * checking prev->state to avoid a wakeup race - thus we
-	 * also have to protect against the task exiting early.
+	 * fast path falls through. We have to clear cpus_runnable before
+	 * checking prev->state to avoid a wakeup race. Protect against
+	 * the task exiting early.
 	 */
 	task_lock(prev);
-	prev->has_cpu = 0;
+	task_release_cpu(prev);
 	mb();
 	if (prev->state == TASK_RUNNING)
 		goto needs_resched;
@@ -505,7 +506,7 @@
 			goto out_unlock;
 
 		spin_lock_irqsave(&runqueue_lock, flags);
-		if ((prev->state == TASK_RUNNING) && !prev->has_cpu)
+		if ((prev->state == TASK_RUNNING) && !task_has_cpu(prev))
 			reschedule_idle(prev);
 		spin_unlock_irqrestore(&runqueue_lock, flags);
 		goto out_unlock;
@@ -545,8 +546,10 @@
 	prev = current;
 	this_cpu = prev->processor;
 
-	if (in_interrupt())
-		goto scheduling_in_interrupt;
+	if (unlikely(in_interrupt())) {
+		printk("Scheduling in interrupt\n");
+		BUG();
+	}
 
 	release_kernel_lock(prev, this_cpu);
 
@@ -559,9 +562,11 @@
 	spin_lock_irq(&runqueue_lock);
 
 	/* move an exhausted RR process to be last.. */
-	if (prev->policy == SCHED_RR)
-		goto move_rr_last;
-move_rr_back:
+	if (unlikely(prev->policy == SCHED_RR))
+		if (!prev->counter) {
+			prev->counter = NICE_TO_TICKS(prev->nice);
+			move_last_runqueue(prev);
+		}
 
 	switch (prev->state) {
 		case TASK_INTERRUPTIBLE:
@@ -585,10 +590,6 @@
 	 */
 	next = idle_task(this_cpu);
 	c = -1000;
-	if (prev->state == TASK_RUNNING)
-		goto still_running;
-
-still_running_back:
 	list_for_each(tmp, &runqueue_head) {
 		p = list_entry(tmp, struct task_struct, run_list);
 		if (can_schedule(p, this_cpu)) {
@@ -599,21 +600,28 @@
 	}
 
 	/* Do we need to re-calculate counters? */
-	if (!c)
-		goto recalculate;
+	if (unlikely(!c)) {
+		struct task_struct *p;
+
+		spin_unlock_irq(&runqueue_lock);
+		read_lock(&tasklist_lock);
+		for_each_task(p)
+			p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice);
+		read_unlock(&tasklist_lock);
+		spin_lock_irq(&runqueue_lock);
+		goto repeat_schedule;
+	}
+
 	/*
 	 * from this point on nothing can prevent us from
 	 * switching to the next task, save this fact in
 	 * sched_data.
 	 */
 	sched_data->curr = next;
-#ifdef CONFIG_SMP
- 	next->has_cpu = 1;
-	next->processor = this_cpu;
-#endif
+	task_set_cpu(next, this_cpu);
 	spin_unlock_irq(&runqueue_lock);
 
-	if (prev == next) {
+	if (unlikely(prev == next)) {
 		/* We won't go through the normal tail, so do this by hand */
 		prev->policy &= ~SCHED_YIELD;
 		goto same_process;
@@ -678,38 +686,6 @@
 	reacquire_kernel_lock(current);
 	if (current->need_resched)
 		goto need_resched_back;
-
-	return;
-
-recalculate:
-	{
-		struct task_struct *p;
-		spin_unlock_irq(&runqueue_lock);
-		read_lock(&tasklist_lock);
-		for_each_task(p)
-			p->counter = (p->counter >> 1) + NICE_TO_TICKS(p->nice);
-		read_unlock(&tasklist_lock);
-		spin_lock_irq(&runqueue_lock);
-	}
-	goto repeat_schedule;
-
-still_running:
-	if (!(prev->cpus_allowed & (1UL << this_cpu)))
-		goto still_running_back;
-	c = goodness(prev, this_cpu, prev->active_mm);
-	next = prev;
-	goto still_running_back;
-
-move_rr_last:
-	if (!prev->counter) {
-		prev->counter = NICE_TO_TICKS(prev->nice);
-		move_last_runqueue(prev);
-	}
-	goto move_rr_back;
-
-scheduling_in_interrupt:
-	printk("Scheduling in interrupt\n");
-	BUG();
 	return;
 }
 
@@ -1072,6 +1048,10 @@
 		if (current->policy == SCHED_OTHER)
 			current->policy |= SCHED_YIELD;
 		current->need_resched = 1;
+
+		spin_lock_irq(&runqueue_lock);
+		move_last_runqueue(current);
+		spin_unlock_irq(&runqueue_lock);
 	}
 	return 0;
 }
@@ -1176,13 +1156,10 @@
 	else
 		printk(" (NOTLB)\n");
 
-#if defined(CONFIG_X86) || defined(CONFIG_SPARC64) || defined(CONFIG_ARM) || defined(CONFIG_ALPHA)
-/* This is very useful, but only works on ARM, x86 and sparc64 right now */
 	{
 		extern void show_trace_task(struct task_struct *tsk);
 		show_trace_task(p);
 	}
-#endif
 }
 
 char * render_sigset_t(sigset_t *set, char *buffer)
diff -urN linux-2.4.15-pre8/kernel/signal.c linux/kernel/signal.c
--- linux-2.4.15-pre8/kernel/signal.c	Mon Sep 17 16:40:01 2001
+++ linux/kernel/signal.c	Mon Dec  3 23:32:46 2001
@@ -479,7 +479,7 @@
 	 * other than doing an extra (lightweight) IPI interrupt.
 	 */
 	spin_lock(&runqueue_lock);
-	if (t->has_cpu && t->processor != smp_processor_id())
+	if (task_has_cpu(t) && t->processor != smp_processor_id())
 		smp_send_reschedule(t->processor);
 	spin_unlock(&runqueue_lock);
 #endif /* CONFIG_SMP */
diff -urN linux-2.4.15-pre8/mm/filemap.c linux/mm/filemap.c
--- linux-2.4.15-pre8/mm/filemap.c	Mon Dec  3 23:32:36 2001
+++ linux/mm/filemap.c	Mon Dec  3 23:32:46 2001
@@ -555,8 +555,13 @@
  */
 int fail_writepage(struct page *page)
 {
-	activate_page(page);
-	SetPageReferenced(page);
+	/* Only activate on memory-pressure, not fsync.. */
+	if (PageLaunder(page)) {
+		activate_page(page);
+		SetPageReferenced(page);
+	}
+
+	/* Set the page dirty again, unlock */
 	SetPageDirty(page);
 	UnlockPage(page);
 	return 0;
@@ -1476,6 +1481,87 @@
 	UPDATE_ATIME(inode);
 }
 
+static ssize_t generic_file_direct_IO(int rw, struct file * filp, char * buf, size_t count, loff_t offset)
+{
+	ssize_t retval;
+	int new_iobuf, chunk_size, blocksize_mask, blocksize, blocksize_bits, iosize, progress;
+	struct kiobuf * iobuf;
+	struct inode * inode = filp->f_dentry->d_inode;
+	struct address_space * mapping = inode->i_mapping;
+
+	new_iobuf = 0;
+	iobuf = filp->f_iobuf;
+	if (test_and_set_bit(0, &filp->f_iobuf_lock)) {
+		/*
+		 * A parallel read/write is using the preallocated iobuf
+		 * so just run slow and allocate a new one.
+		 */
+		retval = alloc_kiovec(1, &iobuf);
+		if (retval)
+			goto out;
+		new_iobuf = 1;
+	}
+
+	blocksize = 1 << inode->i_blkbits;
+	blocksize_bits = inode->i_blkbits;
+	blocksize_mask = blocksize - 1;
+	chunk_size = KIO_MAX_ATOMIC_IO << 10;
+
+	retval = -EINVAL;
+	if ((offset & blocksize_mask) || (count & blocksize_mask))
+		goto out_free;
+	if (!mapping->a_ops->direct_IO)
+		goto out_free;
+
+	/*
+	 * Flush to disk exlusively the _data_, metadata must remains
+	 * completly asynchronous or performance will go to /dev/null.
+	 */
+	filemap_fdatasync(mapping);
+	retval = fsync_inode_data_buffers(inode);
+	filemap_fdatawait(mapping);
+	if (retval < 0)
+		goto out_free;
+
+	progress = retval = 0;
+	while (count > 0) {
+		iosize = count;
+		if (iosize > chunk_size)
+			iosize = chunk_size;
+
+		retval = map_user_kiobuf(rw, iobuf, (unsigned long) buf, iosize);
+		if (retval)
+			break;
+
+		retval = mapping->a_ops->direct_IO(rw, inode, iobuf, (offset+progress) >> blocksize_bits, blocksize);
+
+		if (rw == READ && retval > 0)
+			mark_dirty_kiobuf(iobuf, retval);
+		
+		if (retval >= 0) {
+			count -= retval;
+			buf += retval;
+			progress += retval;
+		}
+
+		unmap_kiobuf(iobuf);
+
+		if (retval != iosize)
+			break;
+	}
+
+	if (progress)
+		retval = progress;
+
+ out_free:
+	if (!new_iobuf)
+		clear_bit(0, &filp->f_iobuf_lock);
+	else
+		free_kiovec(1, &iobuf);
+ out:	
+	return retval;
+}
+
 int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
 {
 	char *kaddr;
@@ -1509,6 +1595,9 @@
 	if ((ssize_t) count < 0)
 		return -EINVAL;
 
+	if (filp->f_flags & O_DIRECT)
+		goto o_direct;
+
 	retval = -EFAULT;
 	if (access_ok(VERIFY_WRITE, buf, count)) {
 		retval = 0;
@@ -1527,7 +1616,29 @@
 				retval = desc.error;
 		}
 	}
+ out:
 	return retval;
+
+ o_direct:
+	{
+		loff_t pos = *ppos, size;
+		struct address_space *mapping = filp->f_dentry->d_inode->i_mapping;
+		struct inode *inode = mapping->host;
+
+		retval = 0;
+		if (!count)
+			goto out; /* skip atime */
+		size = inode->i_size;
+		if (pos < size) {
+			if (pos + count > size)
+				count = size - pos;
+			retval = generic_file_direct_IO(READ, filp, buf, count, pos);
+			if (retval > 0)
+				*ppos = pos + retval;
+		}
+		UPDATE_ATIME(filp->f_dentry->d_inode);
+		goto out;
+	}
 }
 
 static int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset , unsigned long size)
@@ -2765,7 +2876,8 @@
 
 	written = 0;
 
-	if (file->f_flags & O_APPEND)
+	/* FIXME: this is for backwards compatibility with 2.4 */
+	if (!S_ISBLK(inode->i_mode) && file->f_flags & O_APPEND)
 		pos = inode->i_size;
 
 	/*
@@ -2845,6 +2957,9 @@
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	mark_inode_dirty_sync(inode);
 
+	if (file->f_flags & O_DIRECT)
+		goto o_direct;
+
 	do {
 		unsigned long index, offset;
 		long page_fault;
@@ -2921,6 +3036,7 @@
 			status = generic_osync_inode(inode, OSYNC_METADATA|OSYNC_DATA);
 	}
 	
+out_status:	
 	err = written ? written : status;
 out:
 
@@ -2929,6 +3045,25 @@
 fail_write:
 	status = -EFAULT;
 	goto unlock;
+
+o_direct:
+	written = generic_file_direct_IO(WRITE, file, (char *) buf, count, pos);
+	if (written > 0) {
+		loff_t end = pos + written;
+		if (end > inode->i_size && !S_ISBLK(inode->i_mode)) {
+			inode->i_size = end;
+			mark_inode_dirty(inode);
+		}
+		*ppos = end;
+		invalidate_inode_pages2(mapping);
+	}
+	/*
+	 * Sync the fs metadata but not the minor inode changes and
+	 * of course not the data as we did direct DMA for the IO.
+	 */
+	if (written >= 0 && file->f_flags & O_SYNC)
+		status = generic_osync_inode(inode, OSYNC_METADATA);
+	goto out_status;
 }
 
 void __init page_cache_init(unsigned long mempages)
diff -urN linux-2.4.15-pre8/mm/shmem.c linux/mm/shmem.c
--- linux-2.4.15-pre8/mm/shmem.c	Mon Dec  3 23:32:36 2001
+++ linux/mm/shmem.c	Mon Dec  3 23:32:46 2001
@@ -428,6 +428,8 @@
 
 	if (!PageLocked(page))
 		BUG();
+	if (!PageLaunder(page))
+		return fail_writepage(page);
 
 	mapping = page->mapping;
 	index = page->index;
