diff -u --recursive --new-file v1.1.82/linux/CREDITS linux/CREDITS
--- v1.1.82/linux/CREDITS	Fri Jan 13 16:57:03 1995
+++ linux/CREDITS	Wed Jan 18 09:40:45 1995
@@ -32,6 +32,14 @@
 S: University of Calgary
 S: Calgary, Alberta, Canada
 
+N: Ralf Baechle
+E: ralf@waldorf-gmbh.de
+D: Linux/MIPS port
+D: Linux/68k hacker
+S: Hauptstrasse 19
+S: 79837 St. Blasien
+S: Germany
+
 N: Krishna Balasubramanian
 E: balasub@cis.ohio-state.edu
 D: Wrote SYS V IPC (part of standard kernel since 0.99.10)
@@ -629,6 +637,13 @@
 S: FIN-00330 Helsingfors
 S: Finland
 
+N: Michael Neuffer
+E: neuffer@goofy.zdv.uni-mainz.de
+D: developer and maintainer of the eata_dma SCSI driver 
+S: Zum Schiersteiner Grund 2
+S: 55127 Mainz
+S: Germany
+
 N: David C. Niemi
 E: niemidc@clark.net
 E: niemidc@slma.com
@@ -921,6 +936,15 @@
 S: 17 Canterbury Square #101
 S: Alexandria, Virginia 22304
 S: USA
+
+N: Niibe Yutaka
+E: gniibe@mri.co.jp
+D: PLIP driver
+D: Asynchronous socket I/O in the NET code
+S: Mitsubishi Research Institute, Inc.
+S: ARCO Tower 1-8-1 Shimomeguro Meguro-ku
+S: Tokyo 153
+S: Japan
 
 N: Orest Zborowski
 E: orestz@eskimo.com
diff -u --recursive --new-file v1.1.82/linux/Makefile linux/Makefile
--- v1.1.82/linux/Makefile	Mon Jan 16 14:18:12 1995
+++ linux/Makefile	Wed Jan 18 23:33:09 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 82
+SUBLEVEL = 83
 
 ARCH = i386
 
@@ -165,6 +165,7 @@
 	   echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\"; \
 	 fi >> include/linux/version.h
 	@echo \#define LINUX_COMPILER \"`$(HOSTCC) -v 2>&1 | tail -1`\" >> include/linux/version.h
+	@echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> include/linux/version.h
 
 init/version.o: init/version.c include/linux/version.h
 	$(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/boot/Makefile linux/arch/alpha/boot/Makefile
--- v1.1.82/linux/arch/alpha/boot/Makefile	Mon Jan 16 14:18:12 1995
+++ linux/arch/alpha/boot/Makefile	Wed Jan 18 22:52:36 1995
@@ -21,8 +21,8 @@
 
 OBJECTS = head.o main.o
 
-all:
-	@echo Be careful.. This works for me, not for you
+all:	tools/lxboot tools/bootlx vmlinux
+	@echo run mkfloppy on machine with floppy drive
 
 msb:	tools/lxboot tools/bootlx vmlinux
 	( cat tools/lxboot tools/bootlx vmlinux ) > /dev/rz0a
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/boot/main.c linux/arch/alpha/boot/main.c
--- v1.1.82/linux/arch/alpha/boot/main.c	Mon Jan 16 14:18:12 1995
+++ linux/arch/alpha/boot/main.c	Wed Jan 18 22:52:36 1995
@@ -75,13 +75,13 @@
 #define new_vptb (0xfffffffe00000000UL)
 void pal_init(void)
 {
-	unsigned long i, rev;
-	unsigned long *L1;
+	unsigned long i, rev, sum;
+	unsigned long *L1, *l;
 	struct percpu_struct * percpu;
 	struct pcb_struct * pcb_pa;
 
 	/* Find the level 1 page table and duplicate it in high memory */
-	L1 = (unsigned long *) 0x200802000UL;
+	L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */
 	L1[1023] = L1[1];
 
 	percpu = (struct percpu_struct *) (hwrpb.processor_offset + (unsigned long) &hwrpb),
@@ -113,7 +113,15 @@
 		halt();
 	}
 	rev = percpu->pal_revision = percpu->palcode_avail[2];
+
 	hwrpb.vptb = new_vptb;
+
+	/* update checksum: */
+	sum = 0;
+	for (l = (unsigned long *) &hwrpb; l < (unsigned long *) &hwrpb.chksum; ++l)
+		sum += *l;
+	hwrpb.chksum = sum;
+
 	printk("Ok (rev %lx)\n", rev);
 	/* remove the old virtual page-table mapping */
 	L1[1] = 0;
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.1.82/linux/arch/alpha/config.in	Mon Jan 16 14:18:12 1995
+++ linux/arch/alpha/config.in	Wed Jan 18 22:52:36 1995
@@ -9,6 +9,7 @@
 bool 'Normal harddisk support' CONFIG_BLK_DEV_HD n
 bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
 bool 'Networking support' CONFIG_NET n
+bool 'PCI alpha motherboard' CONFIG_PCI n
 bool 'System V IPC' CONFIG_SYSVIPC n
 bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
 
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v1.1.82/linux/arch/alpha/kernel/entry.S	Mon Jan 16 14:18:12 1995
+++ linux/arch/alpha/kernel/entry.S	Wed Jan 18 22:52:36 1995
@@ -87,6 +87,42 @@
 	rti
 .end entMM
 
+.align 5
+.globl	entArith
+.ent	entArith
+entArith:
+	SAVE_ALL
+	bis	$30,$30,$19
+	lda	$27,do_entArith
+	jsr	$26,($27),do_entArith
+	RESTORE_ALL
+	rti
+.end entArith
+
+.align 5
+.globl	entIF
+.ent	entIF
+entIF:
+	SAVE_ALL
+	bis	$30,$30,$19
+	lda	$27,do_entIF
+	jsr	$26,($27),do_entIF
+	RESTORE_ALL
+	rti
+.end entIF
+
+.align 5
+.globl	entUna
+.ent	entUna
+entUna:
+	SAVE_ALL
+	bis	$30,$30,$19
+	lda	$27,do_entUna
+	jsr	$26,($27),do_entUna
+	RESTORE_ALL
+	rti
+.end entUna
+
 	.align 5
 	.globl sys_call_table
 sys_call_table:
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v1.1.82/linux/arch/alpha/kernel/irq.c	Mon Jan 16 14:18:12 1995
+++ linux/arch/alpha/kernel/irq.c	Wed Jan 18 22:52:36 1995
@@ -10,6 +10,7 @@
  * should be easier.
  */
 
+#include <linux/config.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
@@ -139,62 +140,125 @@
 	halt();
 }
 
-static void handle_irq(int irq, struct pt_regs * regs)
+static void handle_nmi(struct pt_regs * regs)
+{
+	printk("Whee.. NMI received. Probable hardware error\n");
+	printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461));
+}
+
+static void unexpected_irq(int irq, struct pt_regs * regs)
+{
+	int i;
+
+	printk("IO device interrupt, irq = %d\n", irq);
+	printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
+	printk("Expecting: ");
+	for (i = 0; i < 16; i++)
+		if (irq_action[i].handler)
+			printk("[%s:%d] ", irq_action[i].name, i);
+	printk("\n");
+	printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n",
+		inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
+	outb(0x0c, 0x3fc);
+	outb(0x0c, 0x2fc);
+	outb(0,0x61);
+	outb(0,0x461);
+}
+
+static inline void handle_irq(int irq, struct pt_regs * regs)
 {
 	struct irqaction * action = irq + irq_action;
 
 	kstat.interrupts[irq]++;
-	if (action->handler)
-		action->handler(irq, regs);
+	if (!action->handler) {
+		unexpected_irq(irq, regs);
+		return;
+	}
+	action->handler(irq, regs);
+}
+
+static void local_device_interrupt(unsigned long vector, struct pt_regs * regs)
+{
+	switch (vector) {
+		/* com1: map to irq 4 */
+		case 0x900:
+			handle_irq(4, regs);
+			return;
+
+		/* com2: map to irq 3 */
+		case 0x920:
+			handle_irq(3, regs);
+			return;
+
+		/* keyboard: map to irq 1 */
+		case 0x980:
+			handle_irq(1, regs);
+			return;
+
+		/* mouse: map to irq 12 */
+		case 0x990:
+			handle_irq(12, regs);
+			return;
+		default:
+			printk("Unknown local interrupt %lx\n", vector);
+	}
 }
 
 /*
- * I don't have any good documentation on the EISA hardware interrupt
- * stuff: I don't know the mapping between the interrupt vector and the
- * EISA interrupt number.
- *
- * It *seems* to be 0x8X0 for EISA interrupt X, and 0x9X0 for the
- * local motherboard interrupts..
+ * The vector is 0x8X0 for EISA interrupt X, and 0x9X0 for the local
+ * motherboard interrupts.. This is for the Jensen.
  *
- *	0x660 - NMI?
+ *	0x660 - NMI
  *
- *	0x800 - ??? I've gotten this, but EISA irq0 shouldn't happen
- *		as the timer is not on the EISA bus
- *
- *	0x860 - ??? floppy disk (EISA irq6)
- *
- *	0x900 - ??? I get this at autoprobing when the EISA serial
- *		lines com3/com4 don't exist. It keeps coming after
- *		that..
+ *	0x800 - IRQ0  interval timer (not used, as we use the RTC timer)
+ *	0x810 - IRQ1  line printer (duh..)
+ *	0x860 - IRQ6  floppy disk
+ *	0x8E0 - IRQ14 SCSI controller
  *
+ *	0x900 - COM1
+ *	0x920 - COM2
  *	0x980 - keyboard
  *	0x990 - mouse
  *
- * We'll see..
+ * The PCI version is more sane: it doesn't have the local interrupts at
+ * all, and has only normal PCI interrupts from devices. Happily it's easy
+ * enough to do a sane mapping from the Jensen.. Note that this means
+ * that we may have to do a hardware "ack" to a different interrupt than
+ * we report to the rest of the world..
  */
 static void device_interrupt(unsigned long vector, struct pt_regs * regs)
 {
-	int i;
-	static int nr = 0;
+	int irq, ack;
 
-	if (vector == 0x980 && irq_action[1].handler) {
-		handle_irq(1, regs);
+	if (vector == 0x660) {
+		handle_nmi(regs);
 		return;
 	}
 
-	if (nr > 3)
+	ack = irq = (vector - 0x800) >> 4;
+#ifndef CONFIG_PCI
+	if (vector >= 0x900) {
+		local_device_interrupt(vector, regs);
 		return;
-	nr++;
-	printk("IO device interrupt, vector = %lx\n", vector);
-	printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
-	printk("Expecting: ");
-	for (i = 0; i < 16; i++)
-		if (irq_action[i].handler)
-			printk("[%s:%d] ", irq_action[i].name, i);
-	printk("\n");
-	printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n",
-		inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
-	printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461));
+	}
+	/* irq1 is supposed to be the keyboard, silly Jensen */
+	if (irq == 1)
+		irq = 7;
+#endif
+	printk("%d%d", irq, ack);
+	if (!irq)
+		printk(".");
+	else
+		handle_irq(irq, regs);
+
+	/* ACK the interrupt making it the lowest priority */
+	/*  First the slave .. */
+	if (ack > 7) {
+		outb(0xC0 | (ack - 8), 0xa0);
+		ack = 2;
+	}
+	/* .. then the master */
+	outb(0xC0 | ack, 0x20);
 }
 
 static void machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs)
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v1.1.82/linux/arch/alpha/kernel/setup.c	Mon Jan 16 14:18:12 1995
+++ linux/arch/alpha/kernel/setup.c	Wed Jan 18 22:52:36 1995
@@ -65,12 +65,12 @@
 	cluster = memdesc->cluster;
 	for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
 		unsigned long tmp;
-		if (cluster->usage & 1)
-			continue;
 		tmp = (cluster->start_pfn + cluster->numpages) << PAGE_SHIFT;
 		if (tmp > high)
 			high = tmp;
 	}
+	/* round it up to an even number of pages.. */
+	high = (high + PAGE_SIZE) & (PAGE_MASK*2);
 	return PAGE_OFFSET + high;
 }
 
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
--- v1.1.82/linux/arch/alpha/kernel/traps.c	Mon Jan 16 14:18:13 1995
+++ linux/arch/alpha/kernel/traps.c	Wed Jan 18 22:52:36 1995
@@ -15,12 +15,33 @@
 	unsigned long i;
 
 	printk("%s %ld\n", str, err);
-	for (i = 0 ; i++ ; i < 500000000)
+	printk("PC = %016lx PS = %04lx\n", regs->pc, regs->ps);
+	for (i = 0 ; i < 5000000000 ; i++)
 		/* pause */;
 	halt();
 }
 
+asmlinkage void do_entArith(unsigned long summary, unsigned long write_mask, unsigned long a2, struct pt_regs * regs)
+{
+	printk("Arithmetic trap: %02lx %016lx\n", summary, write_mask);
+	die_if_kernel("Arithmetic fault", regs, 0);
+}
+
+asmlinkage void do_entIF(unsigned long type, unsigned long a1, unsigned long a2, struct pt_regs * regs)
+{
+	die_if_kernel("Instruction fault", regs, type);
+}
+
+asmlinkage void do_entUna(unsigned long va, unsigned long opcode, unsigned long reg, struct pt_regs * regs)
+{
+	printk("Unaligned trap: %016lx %ld %ld\n", va, opcode, reg);
+	die_if_kernel("Unaligned", regs, 0);
+}
+
 extern asmlinkage void entMM(void);
+extern asmlinkage void entIF(void);
+extern asmlinkage void entArith(void);
+extern asmlinkage void entUna(void);
 
 void trap_init(void)
 {
@@ -30,5 +51,9 @@
 		"___tmp:\tldgp %0,0(%0)"
 		: "=r" (gptr));
 	wrkgp(gptr);
+
+	wrent(entArith, 1);
 	wrent(entMM, 2);
+	wrent(entIF, 3);
+	wrent(entUna, 4);
 }
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/mm/fault.c linux/arch/alpha/mm/fault.c
--- v1.1.82/linux/arch/alpha/mm/fault.c	Mon Jan 16 14:18:13 1995
+++ linux/arch/alpha/mm/fault.c	Wed Jan 18 09:31:40 1995
@@ -44,12 +44,9 @@
 
 	if (mmcsr == 1)
 		goto bad_area;
-	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			goto bad_area;
-		if (vma->vm_end > address)
-			break;
-	}
+	vma = find_vma(current, address);
+	if (!vma)
+		goto bad_area;
 	if (vma->vm_start <= address)
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
diff -u --recursive --new-file v1.1.82/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c
--- v1.1.82/linux/arch/alpha/mm/init.c	Mon Jan 16 14:18:13 1995
+++ linux/arch/alpha/mm/init.c	Tue Jan 17 00:57:01 1995
@@ -91,7 +91,7 @@
  * unmaps the bootup page table (as we're now in KSEG, so we don't need it).
  *
  * The bootup sequence put the virtual page table into high memory: that
- * means that we cah change the L1 page table by just using VL1p below.
+ * means that we can change the L1 page table by just using VL1p below.
  */
 #define VL1p ((unsigned long *) 0xffffffffffffe000)
 unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
diff -u --recursive --new-file v1.1.82/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.1.82/linux/arch/i386/config.in	Mon Jan 16 14:18:13 1995
+++ linux/arch/i386/config.in	Wed Jan 18 21:00:26 1995
@@ -187,6 +187,7 @@
     fi
   fi
 fi
+bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
 
 comment 'Filesystems'
 
diff -u --recursive --new-file v1.1.82/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- v1.1.82/linux/arch/i386/kernel/bios32.c	Mon Jan 16 14:18:13 1995
+++ linux/arch/i386/kernel/bios32.c	Tue Jan 17 00:57:01 1995
@@ -550,17 +550,17 @@
 	for (i=0;i<OPTIMISATION_NUM;i++)
 	{
 		printk("    %s : ",optimisation[i].type);
-		if (bridge_mapping[pos+i].adress==0) printk("Not supported.");
+		if (bridge_mapping[pos+i].address==0) printk("Not supported.");
 		else {
 			pcibios_read_config_byte(
-				bus, dev_fn, bridge_mapping[pos+i].adress, &val);
+				bus, dev_fn, bridge_mapping[pos+i].address, &val);
 			if ((val & bridge_mapping[pos+i].mask)==bridge_mapping[pos+i].value) 
 			{
 				printk("%s.",optimisation[i].on);
 				if (turn_on==0) 
 				{
 				pcibios_write_config_byte(
-					bus, dev_fn, bridge_mapping[pos+i].adress,
+					bus, dev_fn, bridge_mapping[pos+i].address,
 					(val | bridge_mapping[pos+i].mask) -
 					bridge_mapping[pos+i].value);
 				printk("Changed! now %s.",optimisation[i].off);
@@ -570,7 +570,7 @@
 				if (turn_on==1) 
 				{
 				pcibios_write_config_byte(
-					bus, dev_fn, bridge_mapping[pos+i].adress,
+					bus, dev_fn, bridge_mapping[pos+i].address,
 					(val & (0xff-bridge_mapping[pos+i].mask)) +
 					bridge_mapping[pos+i].value);
 				printk("Changed! now %s.",optimisation[i].on);
@@ -644,7 +644,7 @@
 	{
 		printk("Unknown PCI device. PCI Vendor id=%x. PCI Device id=%x.\n",
 			vendor_id & 0xffff,device_id & 0xffff);
-		printk("PLEASE MAIL POTTER@CAO-VLSI.IBP.FR your harware description and /proc/pci.\n");
+		printk("PLEASE MAIL POTTER@CAO-VLSI.IBP.FR your hardware description and /proc/pci.\n");
 		return;
 	}
 	/*
diff -u --recursive --new-file v1.1.82/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v1.1.82/linux/arch/i386/kernel/process.c	Mon Jan 16 14:18:13 1995
+++ linux/arch/i386/kernel/process.c	Tue Jan 17 11:23:12 1995
@@ -53,6 +53,8 @@
  */
 void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp)
 {
+	regs->cs = USER_CS;
+	regs->ds = regs->es = regs->ss = regs->fs = regs->gs = USER_DS;
 	regs->eip = eip;
 	regs->esp = esp;
 }
diff -u --recursive --new-file v1.1.82/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v1.1.82/linux/arch/i386/kernel/ptrace.c	Fri Jan 13 10:12:22 1995
+++ linux/arch/i386/kernel/ptrace.c	Wed Jan 18 09:31:40 1995
@@ -152,17 +152,14 @@
 	} 
 }
 
-static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr)
+static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
 {
 	struct vm_area_struct * vma;
 
 	addr &= PAGE_MASK;
-	for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			return NULL;
-		if (vma->vm_end > addr)
-			break;
-	}
+	vma = find_vma(tsk,addr);
+	if (!vma)
+		return NULL;
 	if (vma->vm_start <= addr)
 		return vma;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
@@ -181,7 +178,7 @@
 static int read_long(struct task_struct * tsk, unsigned long addr,
 	unsigned long * result)
 {
-	struct vm_area_struct * vma = find_vma(tsk, addr);
+	struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
 	if (!vma)
 		return -EIO;
@@ -223,7 +220,7 @@
 static int write_long(struct task_struct * tsk, unsigned long addr,
 	unsigned long data)
 {
-	struct vm_area_struct * vma = find_vma(tsk, addr);
+	struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
 	if (!vma)
 		return -EIO;
diff -u --recursive --new-file v1.1.82/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v1.1.82/linux/arch/i386/mm/fault.c	Mon Jan 16 14:18:13 1995
+++ linux/arch/i386/mm/fault.c	Wed Jan 18 09:31:40 1995
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/ptrace.h>
 #include <linux/mman.h>
+#include <linux/mm.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -33,12 +34,9 @@
 
 	/* get the address */
 	__asm__("movl %%cr2,%0":"=r" (address));
-	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			goto bad_area;
-		if (vma->vm_end > address)
-			break;
-	}
+	vma = find_vma(current, address);
+	if (!vma)
+		goto bad_area;
 	if (vma->vm_start <= address)
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
diff -u --recursive --new-file v1.1.82/linux/arch/mips/boot/Makefile linux/arch/mips/boot/Makefile
--- v1.1.82/linux/arch/mips/boot/Makefile	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/boot/Makefile	Tue Jan 17 00:57:01 1995
@@ -10,7 +10,7 @@
 #
 
 #
-# Fake compessed boot
+# Fake compressed boot
 #
 zImage:	$(CONFIGURE) $(TOPDIR)/vmlinux
 	ln -fs $(TOPDIR)/vmlinux zImage
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/Makefile linux/arch/mips/kernel/Makefile
--- v1.1.82/linux/arch/mips/kernel/Makefile	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/Makefile	Wed Jan 18 08:54:12 1995
@@ -19,7 +19,7 @@
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
 OBJS  = process.o signal.o entry.o traps.o irq.o ptrace.o cache.o resume.o \
-	ioport.o bootinfo.o
+	ioport.o setup.o bios32.o
 
 all: kernel.o head.o
 
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/bios32.c linux/arch/mips/kernel/bios32.c
--- v1.1.82/linux/arch/mips/kernel/bios32.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/mips/kernel/bios32.c	Wed Jan 18 08:54:12 1995
@@ -0,0 +1,7 @@
+/*
+ * bios 32 replacement
+ */
+unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
+{
+	return memory_start;
+}
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/bootinfo.c linux/arch/mips/kernel/bootinfo.c
--- v1.1.82/linux/arch/mips/kernel/bootinfo.c	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/bootinfo.c	Thu Jan  1 02:00:00 1970
@@ -1,14 +0,0 @@
-/*
- * arch/mips/kernel/bootinfo.c
- *
- * Copyright (C) 1995  Ralf Baechle
- *
- * Kernel data passed by the loader 
- */
-#include <asm/bootinfo.h>
-
-/*
- * Initialise this structure so that it will be placed in the
- * .data section of the object file
- */
-struct bootinfo boot_info = BOOT_INFO;
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/entry.S linux/arch/mips/kernel/entry.S
--- v1.1.82/linux/arch/mips/kernel/entry.S	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/entry.S	Wed Jan 18 08:54:12 1995
@@ -41,6 +41,10 @@
 		.set	noreorder
 		.align 4
 handle_bottom_half:
+		/*
+		 * If your assembler breaks on the next line it's
+		 * time to update!
+		 */
 		lui	s0,%hi(_intr_count)
 		lw	s1,%lo(_intr_count)(s0)
 		mfc0	s3,CP0_STATUS			# Enable IRQs
@@ -286,7 +290,7 @@
 		.align	5
 spurious_interrupt:
 		/*
-		 * Nothing happend... (whistle)
+		 * Nothing happened... (whistle)
 		 */
 		lui	t1,%hi(_spurious_count)
 		lw	t0,%lo(_spurious_count)(t1)
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/head.S linux/arch/mips/kernel/head.S
--- v1.1.82/linux/arch/mips/kernel/head.S	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/head.S	Wed Jan 18 08:54:13 1995
@@ -306,7 +306,7 @@
 		mtc0	zero,CP0_INDEX
 		la	t0,TLB_ROOT
 		dmtc0	t0,CP0_ENTRYHI
-		li	t0,_swapper_pg_dir
+		la	t0,_swapper_pg_dir
 		srl	t0,t0,6
 		ori	t0,t0,MODE_ALIAS	# uncachable, dirty, valid
 		dmtc0	t0,CP0_ENTRYLO0
@@ -315,8 +315,9 @@
 		/*
 		 * Make page zero unaccessible to catch zero references
 		 */
+		la	t0,_pg0
 		li	t0,KERNELBASE
-		addiu	t0,_pg0
+		addu	t0,t1
 		sw	zero,(t0)
 		/*
 		 * Load the context register with a value that allows
@@ -437,7 +438,7 @@
 
 		.data
 /*
- * Instead of Intel's strage and unportable segment descriptor magic
+ * Instead of Intel's strange and unportable segment descriptor magic
  * we difference user and kernel space by their address.
  * Kernel space (== physical memory) is mapped at KSEG[01],
  * User space is mapped at 0x0.
@@ -446,7 +447,7 @@
 _segment_fs:	.word	KERNEL_DS
 
 /*
- * Inital mapping tables for supported Mips boards.
+ * Initial mapping tables for supported Mips boards.
  * First item is always the number of wired TLB entries,
  * following by EntryHi/EntryLo pairs and page mask.
  * Since everything must be quad-aligned (8) we insert
@@ -501,7 +502,7 @@
  * Initial mapping for ACER PICA-61 boards.
  * FIXME: These are rather preliminary since many drivers,
  * such as serial, parallel, scsi and ethernet need some
- * changes to distuingish between "local" (built-in) and
+ * changes to distinguish between "local" (built-in) and
  * "optional" (ISA/PCI) I/O hardware.
  * Local video ram is mapped to the same location as the
  * bios maps it to. Console driver has been changed
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/ioport.c linux/arch/mips/kernel/ioport.c
--- v1.1.82/linux/arch/mips/kernel/ioport.c	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/ioport.c	Wed Jan 18 08:54:13 1995
@@ -15,10 +15,6 @@
 	struct resource_entry_t *next;
 } resource_entry_t;
 
-static resource_entry_t iolist = { 0, 0, "", NULL };
-
-static resource_entry_t iotable[IOTABLE_SIZE];
-
 /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
 static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
 {
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/irq.c linux/arch/mips/kernel/irq.c
--- v1.1.82/linux/arch/mips/kernel/irq.c	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/irq.c	Wed Jan 18 08:54:13 1995
@@ -1,5 +1,5 @@
 /*
- *	linux/kernel/irq.c
+ *	linux/arch/mips/kernel/irq.c
  *
  *	Copyright (C) 1992 Linus Torvalds
  *
@@ -12,14 +12,7 @@
 
 /*
  * IRQ's are in fact implemented a bit like signal handlers for the kernel.
- * The same sigaction struct is used, and with similar semantics (ie there
- * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there
- * are similarities.
- *
- * sa_handler(int irq_NR) is the default function called (0 if no).
- * sa_mask is horribly ugly (I won't even mention it)
- * sa_flags contains various info: SA_INTERRUPT etc
- * sa_restorer is the unused
+ * Naturally it's not a 1:1 relation, but there are similarities.
  */
 
 #include <linux/ptrace.h>
@@ -28,6 +21,7 @@
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
+#include <linux/timex.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -90,7 +84,14 @@
 /*
  * Initial irq handlers.
  */
-static struct sigaction irq_sigaction[16] = {
+struct irqaction {
+	void (*handler)(int, struct pt_regs *);
+	unsigned long flags;
+	unsigned long mask;
+	const char *name;
+};
+
+static struct irqaction irq_action[16] = {
 	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
 	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
 	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
@@ -104,15 +105,15 @@
 int get_irq_list(char *buf)
 {
 	int i, len = 0;
-	struct sigaction * sa = irq_sigaction;
+	struct irqaction * action = irq_action;
 
-	for (i = 0 ; i < 16 ; i++, sa++) {
-		if (!sa->sa_handler)
+	for (i = 0 ; i < 16 ; i++, action++) {
+		if (!action->handler)
 			continue;
 		len += sprintf(buf+len, "%2d: %8d %c %s\n",
 			i, kstat.interrupts[i],
-			(sa->sa_flags & SA_INTERRUPT) ? '+' : ' ',
-			(char *) sa->sa_mask);
+			(action->flags & SA_INTERRUPT) ? '+' : ' ',
+			action->name);
 	}
 	return len;
 }
@@ -126,10 +127,10 @@
  */
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
-	struct sigaction * sa = irq + irq_sigaction;
+	struct irqaction * action = irq + irq_action;
 
 	kstat.interrupts[irq]++;
-	sa->sa_handler((int) regs);
+	action->handler(irq, regs);
 }
 
 /*
@@ -139,38 +140,38 @@
  */
 asmlinkage void do_fast_IRQ(int irq)
 {
-	struct sigaction * sa = irq + irq_sigaction;
+	struct irqaction * action = irq + irq_action;
 
 	kstat.interrupts[irq]++;
-	sa->sa_handler(irq);
+	action->handler(irq, NULL);
 }
 
 #define SA_PROBE SA_ONESHOT
 
-/*
- * Using "struct sigaction" is slightly silly, but there
- * are historical reasons and it works well, so..
- */
-static int irqaction(unsigned int irq, struct sigaction * new_sa)
+int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+	unsigned long irqflags, const char * devname)
 {
-	struct sigaction * sa;
+	struct irqaction * action;
 	unsigned long flags;
 
 	if (irq > 15)
 		return -EINVAL;
-	sa = irq + irq_sigaction;
-	if (sa->sa_handler)
+	action = irq + irq_action;
+	if (action->handler)
 		return -EBUSY;
-	if (!new_sa->sa_handler)
+	if (!handler)
 		return -EINVAL;
 	save_flags(flags);
 	cli();
-	*sa = *new_sa;
-	/*
-	 * FIXME: Does the SA_INTERRUPT flag make any sense on the MIPS???
-	 */
-	if (!(sa->sa_flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
-		if (sa->sa_flags & SA_INTERRUPT)
+	action->handler = handler;
+	action->flags = irqflags;
+	action->mask = 0;
+	action->name = devname;
+	if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
+		/*
+		 * FIXME: Does the SA_INTERRUPT flag make any sense on MIPS???
+		 */
+		if (action->flags & SA_INTERRUPT)
 			set_intr_gate(irq,fast_interrupt);
 		else
 			set_intr_gate(irq,interrupt);
@@ -188,28 +189,16 @@
 	return 0;
 }
 
-int request_irq(unsigned int irq, void (*handler)(int),
-	unsigned long flags, const char * devname)
-{
-	struct sigaction sa;
-
-	sa.sa_handler = handler;
-	sa.sa_flags = flags;
-	sa.sa_mask = (unsigned long) devname;
-	sa.sa_restorer = NULL;
-	return irqaction(irq,&sa);
-}
-
 void free_irq(unsigned int irq)
 {
-	struct sigaction * sa = irq + irq_sigaction;
+	struct irqaction * action = irq + irq_action;
 	unsigned long flags;
 
 	if (irq > 15) {
 		printk("Trying to free IRQ%d\n",irq);
 		return;
 	}
-	if (!sa->sa_handler) {
+	if (!action->handler) {
 		printk("Trying to free free IRQ%d\n",irq);
 		return;
 	}
@@ -223,26 +212,14 @@
 		outb(cache_A1,0xA1);
 	}
 	set_intr_gate(irq,bad_interrupt);
-	sa->sa_handler = NULL;
-	sa->sa_flags = 0;
-	sa->sa_mask = 0;
-	sa->sa_restorer = NULL;
+	action->handler = NULL;
+	action->flags = 0;
+	action->mask = 0;
+	action->name = NULL;
 	restore_flags(flags);
 }
 
-#if 0
-/*
- * handle fpu errors
- */
-static void math_error_irq(int cpl)
-{
-	if (!hard_math)
-		return;
-	handle_fpe();
-}
-#endif
-
-static void no_action(int cpl) { }
+static void no_action(int cpl, struct pt_regs * regs) { }
 
 unsigned int probe_irq_on (void)
 {
@@ -300,6 +277,10 @@
 {
 	int i;
 
+	/* set the clock to 100 Hz */
+	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
+	outb_p(LATCH & 0xff , 0x40);	/* LSB */
+	outb(LATCH >> 8 , 0x40);	/* MSB */
 	for (i = 0; i < 16 ; i++)
 		set_intr_gate(i, bad_interrupt);
 	if (request_irq(2, no_action, SA_INTERRUPT, "cascade"))
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/process.c linux/arch/mips/kernel/process.c
--- v1.1.82/linux/arch/mips/kernel/process.c	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/process.c	Wed Jan 18 08:54:13 1995
@@ -27,17 +27,6 @@
 #include <asm/mipsconfig.h>
 #include <asm/stackframe.h>
 
-/*
- * Tell us the machine setup..
- */
-#pragma char wp_works_ok = 0;		/* set if paging hardware honours WP */ 
-char wait_available;		/* set if the "wait" instruction available */
-
-/*
- * Bus types ..
- */
-int EISA_bus = 0;
-
 asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
 
 /*
@@ -115,7 +104,7 @@
 
 	/*
 	 * New tasks loose permission to use the fpu. This accelerates context
-	 * switching for non fp programms, which true for the most programms.
+	 * switching for non fp programs, which true for the most programs.
 	 */
 	p->tss.cp0_status = regs->cp0_status &
 	                    ~(ST0_CU1|ST0_CU0|ST0_KSU|ST0_ERL|ST0_EXL);
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/ptrace.c linux/arch/mips/kernel/ptrace.c
--- v1.1.82/linux/arch/mips/kernel/ptrace.c	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/ptrace.c	Wed Jan 18 09:31:40 1995
@@ -152,17 +152,14 @@
 	} 
 }
 
-static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr)
+static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
 {
 	struct vm_area_struct * vma;
 
 	addr &= PAGE_MASK;
-	for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			return NULL;
-		if (vma->vm_end > addr)
-			break;
-	}
+	vma = find_vma(tsk, addr);
+	if (!vma)
+		return NULL;
 	if (vma->vm_start <= addr)
 		return vma;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
@@ -181,7 +178,7 @@
 static int read_long(struct task_struct * tsk, unsigned long addr,
 	unsigned long * result)
 {
-	struct vm_area_struct * vma = find_vma(tsk, addr);
+	struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
 	if (!vma)
 		return -EIO;
@@ -223,7 +220,7 @@
 static int write_long(struct task_struct * tsk, unsigned long addr,
 	unsigned long data)
 {
-	struct vm_area_struct * vma = find_vma(tsk, addr);
+	struct vm_area_struct * vma = find_extend_vma(tsk, addr);
 
 	if (!vma)
 		return -EIO;
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/setup.c linux/arch/mips/kernel/setup.c
--- v1.1.82/linux/arch/mips/kernel/setup.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/mips/kernel/setup.c	Wed Jan 18 08:54:13 1995
@@ -0,0 +1,114 @@
+/*
+ *  linux/arch/mips/kernel/setup.c
+ *
+ *  Copyright (C) 1995  Linus Torvalds
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/ldt.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+
+#include <asm/bootinfo.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*
+ * Tell us the machine setup..
+ */
+char wait_available;		/* set if the "wait" instruction available */
+
+/*
+ * Bus types ..
+ */
+int EISA_bus = 0;
+
+/*
+ * Setup options
+ */
+struct drive_info_struct drive_info;
+struct screen_info screen_info;
+
+unsigned char aux_device_present;
+extern int ramdisk_size;
+extern int root_mountflags;
+extern int end;
+
+extern char empty_zero_page[PAGE_SIZE];
+
+/*
+ * Initialise this structure so that it will be placed in the
+ * .data section of the object file
+ */
+struct bootinfo boot_info = BOOT_INFO;
+
+/*
+ * This is set up by the setup-routine at boot-time
+ */
+#define PARAM	empty_zero_page
+#define EXT_MEM (boot_info.memupper)
+#define DRIVE_INFO (boot_info.drive_info)
+#define SCREEN_INFO (screen_info)
+#define MOUNT_ROOT_RDONLY (boot_info.mount_root_rdonly)
+#define RAMDISK_SIZE (boot_info.ramdisk_size)
+#if 0
+#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
+#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))
+#endif
+#define COMMAND_LINE (boot_info.command_line)
+
+static char command_line[CL_SIZE] = { 0, };
+
+void setup_arch(char **cmdline_p,
+	unsigned long * memory_start_p, unsigned long * memory_end_p)
+{
+	unsigned long memory_start, memory_end;
+	char c = ' ', *to = command_line, *from = COMMAND_LINE;
+	int len = 0;
+
+#if 0
+ 	ROOT_DEV = ORIG_ROOT_DEV;
+#endif
+ 	drive_info = DRIVE_INFO;
+ 	screen_info = SCREEN_INFO;
+#if 0
+	aux_device_present = AUX_DEVICE_INFO;
+#endif
+	memory_end = EXT_MEM;
+	memory_end &= PAGE_MASK;
+	ramdisk_size = RAMDISK_SIZE;
+	if (MOUNT_ROOT_RDONLY)
+		root_mountflags |= MS_RDONLY;
+	memory_start = (unsigned long) &end - KERNELBASE;
+
+	for (;;) {
+		if (c == ' ' && *(unsigned long *)from == *(unsigned long *)"mem=") {
+			memory_end = simple_strtoul(from+4, &from, 0);
+			if ( *from == 'K' || *from == 'k' ) {
+				memory_end = memory_end << 10;
+				from++;
+			} else if ( *from == 'M' || *from == 'm' ) {
+				memory_end = memory_end << 20;
+				from++;
+			}
+		}
+		c = *(from++);
+		if (!c)
+			break;
+		if (CL_SIZE <= ++len)
+			break;
+		*(to++) = c;
+	}
+	*to = '\0';
+	*cmdline_p = command_line;
+	*memory_start_p = memory_start;
+	*memory_end_p = memory_end;
+}
diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/traps.c linux/arch/mips/kernel/traps.c
--- v1.1.82/linux/arch/mips/kernel/traps.c	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/kernel/traps.c	Tue Jan 17 00:57:01 1995
@@ -244,7 +244,7 @@
 		case 0x44000000:
 		case 0xc4000000:
 		case 0xe4000000:
-			printk("CP1 instruction - enabeling cp1.\n");
+			printk("CP1 instruction - enabling cp1.\n");
 			regs->cp0_status |= ST0_CU1;
 			/*
 			 * No need to handle branch delay slots
@@ -252,7 +252,7 @@
 			break;
 		default:
 			/*
-			 * This wasn't a cp1 instruction and therfore illegal.
+			 * This wasn't a cp1 instruction and therefore illegal.
 			 * Default is to kill the process.
 			 */
 			send_sig(SIGILL, current, 1);
@@ -289,7 +289,7 @@
 void do_reserved(struct pt_regs *regs)
 {
 	/*
-	 * Game over - no way to handle this if it ever occours.
+	 * Game over - no way to handle this if it ever occurs.
 	 * Most probably caused by a new unknown cpu type or a
 	 * after another deadly hard/software error.
 	 */
diff -u --recursive --new-file v1.1.82/linux/arch/mips/mm/Makefile linux/arch/mips/mm/Makefile
--- v1.1.82/linux/arch/mips/mm/Makefile	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/mm/Makefile	Wed Jan 18 08:54:13 1995
@@ -14,7 +14,7 @@
 .c.s:
 	$(CC) $(CFLAGS) -S $<
 
-OBJS	= fault.o
+OBJS	= fault.o init.o
 
 mm.o: $(OBJS)
 	$(LD) -r -o mm.o $(OBJS)
diff -u --recursive --new-file v1.1.82/linux/arch/mips/mm/fault.c linux/arch/mips/mm/fault.c
--- v1.1.82/linux/arch/mips/mm/fault.c	Mon Jan 16 14:18:14 1995
+++ linux/arch/mips/mm/fault.c	Wed Jan 18 09:31:40 1995
@@ -1,5 +1,5 @@
 /*
- *  arch/mips/mm/memory.c
+ *  arch/mips/mm/fault.c
  *
  *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
  *  Ported to MIPS by Ralf Baechle
@@ -20,12 +20,7 @@
 #include <asm/segment.h>
 #include <asm/mipsconfig.h>
 
-extern unsigned long pg0[1024];		/* page table for 0-4MB for everybody */
-
-extern void scsi_mem_init(unsigned long);
-extern void sound_mem_init(void);
-extern void die_if_kernel(char *,struct pt_regs *,long);
-extern void show_net_buffers(void);
+extern void die_if_kernel(char *, struct pt_regs *, long);
 
 /*
  * This routine handles page faults.  It determines the address,
@@ -42,12 +37,9 @@
 	__asm__("dmfc0\t%0,$8"
 	        : "=r" (address));
 
-	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			goto bad_area;
-		if (vma->vm_end > address)
-			break;
-	}
+	vma = find_vma(current, address);
+	if (!vma)
+		goto bad_area;
 	if (vma->vm_start <= address)
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
@@ -116,228 +108,4 @@
 	}
 	die_if_kernel("Oops", regs, error_code);
 	do_exit(SIGKILL);
-}
-
-/*
- * BAD_PAGE is the page that is used for page faults when linux
- * is out-of-memory. Older versions of linux just did a
- * do_exit(), but using this instead means there is less risk
- * for a process dying in kernel mode, possibly leaving a inode
- * unused etc..
- *
- * BAD_PAGETABLE is the accompanying page-table: it is initialized
- * to point to BAD_PAGE entries.
- *
- * ZERO_PAGE is a special page that is used for zero-initialized
- * data and COW.
- */
-unsigned long __bad_pagetable(void)
-{
-	extern char empty_bad_page_table[PAGE_SIZE];
-	unsigned long dummy;
-
-	__asm__ __volatile__(
-		".set\tnoreorder\n\t"
-		"1:\tsw\t%2,(%0)\n\t"
-		"subu\t%1,%1,1\n\t"
-		"bne\t$0,%1,1b\n\t"
-		"addiu\t%0,%0,1\n\t"
-		".set\treorder"
-		:"=r" (dummy),
-		 "=r" (dummy)
-		:"r" (BAD_PAGE + PAGE_TABLE),
-		 "0" ((long) empty_bad_page_table),
-		 "1" (PTRS_PER_PAGE));
-
-	return (unsigned long) empty_bad_page_table;
-}
-
-unsigned long __bad_page(void)
-{
-	extern char empty_bad_page[PAGE_SIZE];
-	unsigned long dummy;
-
-	__asm__ __volatile__(
-		".set\tnoreorder\n\t"
-		"1:\tsw\t$0,(%0)\n\t"
-		"subu\t%1,%1,1\n\t"
-		"bne\t$0,%1,1b\n\t"
-		"addiu\t%0,%0,1\n\t"
-		".set\treorder"
-		:"=r" (dummy),
-		 "=r" (dummy)
-		:"0" ((long) empty_bad_page),
-		 "1" (PTRS_PER_PAGE));
-
-	return (unsigned long) empty_bad_page;
-}
-
-unsigned long __zero_page(void)
-{
-	extern char empty_zero_page[PAGE_SIZE];
-	unsigned long dummy;
-
-	__asm__ __volatile__(
-		".set\tnoreorder\n\t"
-		"1:\tsw\t$0,(%0)\n\t"
-		"subu\t%1,%1,1\n\t"
-		"bne\t$0,%1,1b\n\t"
-		"addiu\t%0,%0,1\n\t"
-		".set\treorder"
-		:"=r" (dummy),
-		 "=r" (dummy)
-		:"0" ((long) empty_zero_page),
-		 "1" (PTRS_PER_PAGE));
-
-	return (unsigned long) empty_zero_page;
-}
-
-void show_mem(void)
-{
-	int i,free = 0,total = 0,reserved = 0;
-	int shared = 0;
-
-	printk("Mem-info:\n");
-	show_free_areas();
-	printk("Free swap:       %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
-	i = high_memory >> PAGE_SHIFT;
-	while (i-- > 0) {
-		total++;
-		if (mem_map[i] & MAP_PAGE_RESERVED)
-			reserved++;
-		else if (!mem_map[i])
-			free++;
-		else
-			shared += mem_map[i]-1;
-	}
-	printk("%d pages of RAM\n",total);
-	printk("%d free pages\n",free);
-	printk("%d reserved pages\n",reserved);
-	printk("%d pages shared\n",shared);
-	show_buffers();
-#ifdef CONFIG_NET
-	show_net_buffers();
-#endif
-}
-
-extern unsigned long free_area_init(unsigned long, unsigned long);
-
-/*
- * paging_init() sets up the page tables - note that the first 4MB are
- * already mapped by head.S.
- *
- * This routines also unmaps the page at virtual kernel address 0, so
- * that we can trap those pesky NULL-reference errors in the kernel.
- */
-unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
-{
-	unsigned long * pg_dir;
-	unsigned long * pg_table;
-	unsigned long tmp;
-	unsigned long address;
-
-	start_mem = PAGE_ALIGN(start_mem);
-	address = 0;
-	pg_dir = swapper_pg_dir;
-	while (address < end_mem) {
-		tmp = *pg_dir;
-		tmp &= PAGE_MASK;
-		if (!tmp) {
-			tmp = start_mem;
-			start_mem += PAGE_SIZE;
-		}
-		/*
-		 * also map it in at 0x00000000 for init
-		 */
-		*pg_dir = tmp | PAGE_TABLE;
-		pg_dir++;
-		pg_table = (unsigned long *) (tmp & PAGE_MASK);
-		for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) {
-			if (address < end_mem)
-				*pg_table = address | PAGE_SHARED;
-			else
-				*pg_table = 0;
-			address += PAGE_SIZE;
-		}
-	}
-#if KERNELBASE == KSEG0
-	cacheflush();
-#endif
-	invalidate();
-	return free_area_init(start_mem, end_mem);
-}
-
-void mem_init(unsigned long start_mem, unsigned long end_mem)
-{
-	int codepages = 0;
-	int reservedpages = 0;
-	int datapages = 0;
-	unsigned long tmp;
-	extern int etext;
-
-	end_mem &= PAGE_MASK;
-	high_memory = end_mem;
-
-	/* mark usable pages in the mem_map[] */
-	start_mem = PAGE_ALIGN(start_mem);
-
-	while (start_mem < high_memory) {
-		mem_map[MAP_NR(start_mem)] = 0;
-		start_mem += PAGE_SIZE;
-	}
-#ifdef CONFIG_SCSI
-	scsi_mem_init(high_memory);
-#endif
-#ifdef CONFIG_SOUND
-	sound_mem_init();
-#endif
-	for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) {
-		if (mem_map[MAP_NR(tmp)]) {
-			/*
-			 * We don't have any reserved pages on the
-			 * MIPS systems supported until now
-			 */
-			if (0)
-				reservedpages++;
-			else if (tmp < ((unsigned long) &etext - KERNELBASE))
-				codepages++;
-			else
-				datapages++;
-			continue;
-		}
-		mem_map[MAP_NR(tmp)] = 1;
-		free_page(tmp);
-	}
-	tmp = nr_free_pages << PAGE_SHIFT;
-	printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
-		tmp >> 10,
-		high_memory >> 10,
-		codepages << (PAGE_SHIFT-10),
-		reservedpages << (PAGE_SHIFT-10),
-		datapages << (PAGE_SHIFT-10));
-
-	invalidate();
-	return;
-}
-
-void si_meminfo(struct sysinfo *val)
-{
-	int i;
-
-	i = high_memory >> PAGE_SHIFT;
-	val->totalram = 0;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages << PAGE_SHIFT;
-	val->bufferram = buffermem;
-	while (i-- > 0)  {
-		if (mem_map[i] & MAP_PAGE_RESERVED)
-			continue;
-		val->totalram++;
-		if (!mem_map[i])
-			continue;
-		val->sharedram += mem_map[i]-1;
-	}
-	val->totalram <<= PAGE_SHIFT;
-	val->sharedram <<= PAGE_SHIFT;
-	return;
 }
diff -u --recursive --new-file v1.1.82/linux/arch/mips/mm/init.c linux/arch/mips/mm/init.c
--- v1.1.82/linux/arch/mips/mm/init.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/mips/mm/init.c	Wed Jan 18 08:54:13 1995
@@ -0,0 +1,252 @@
+/*
+ *  arch/mips/mm/init.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *  Ported to MIPS by Ralf Baechle
+ */
+
+#include <linux/config.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/mipsconfig.h>
+
+extern unsigned long pg0[1024];		/* page table for 0-4MB for everybody */
+
+extern void scsi_mem_init(unsigned long);
+extern void sound_mem_init(void);
+extern void die_if_kernel(char *,struct pt_regs *,long);
+extern void show_net_buffers(void);
+
+/*
+ * BAD_PAGE is the page that is used for page faults when linux
+ * is out-of-memory. Older versions of linux just did a
+ * do_exit(), but using this instead means there is less risk
+ * for a process dying in kernel mode, possibly leaving a inode
+ * unused etc..
+ *
+ * BAD_PAGETABLE is the accompanying page-table: it is initialized
+ * to point to BAD_PAGE entries.
+ *
+ * ZERO_PAGE is a special page that is used for zero-initialized
+ * data and COW.
+ */
+unsigned long __bad_pagetable(void)
+{
+	extern char empty_bad_page_table[PAGE_SIZE];
+	unsigned long dummy;
+
+	__asm__ __volatile__(
+		".set\tnoreorder\n\t"
+		"1:\tsw\t%2,(%0)\n\t"
+		"subu\t%1,%1,1\n\t"
+		"bne\t$0,%1,1b\n\t"
+		"addiu\t%0,%0,1\n\t"
+		".set\treorder"
+		:"=r" (dummy),
+		 "=r" (dummy)
+		:"r" (BAD_PAGE + PAGE_TABLE),
+		 "0" ((long) empty_bad_page_table),
+		 "1" (PTRS_PER_PAGE));
+
+	return (unsigned long) empty_bad_page_table;
+}
+
+unsigned long __bad_page(void)
+{
+	extern char empty_bad_page[PAGE_SIZE];
+	unsigned long dummy;
+
+	__asm__ __volatile__(
+		".set\tnoreorder\n\t"
+		"1:\tsw\t$0,(%0)\n\t"
+		"subu\t%1,%1,1\n\t"
+		"bne\t$0,%1,1b\n\t"
+		"addiu\t%0,%0,1\n\t"
+		".set\treorder"
+		:"=r" (dummy),
+		 "=r" (dummy)
+		:"0" ((long) empty_bad_page),
+		 "1" (PTRS_PER_PAGE));
+
+	return (unsigned long) empty_bad_page;
+}
+
+unsigned long __zero_page(void)
+{
+	extern char empty_zero_page[PAGE_SIZE];
+	unsigned long dummy;
+
+	__asm__ __volatile__(
+		".set\tnoreorder\n\t"
+		"1:\tsw\t$0,(%0)\n\t"
+		"subu\t%1,%1,1\n\t"
+		"bne\t$0,%1,1b\n\t"
+		"addiu\t%0,%0,1\n\t"
+		".set\treorder"
+		:"=r" (dummy),
+		 "=r" (dummy)
+		:"0" ((long) empty_zero_page),
+		 "1" (PTRS_PER_PAGE));
+
+	return (unsigned long) empty_zero_page;
+}
+
+void show_mem(void)
+{
+	int i,free = 0,total = 0,reserved = 0;
+	int shared = 0;
+
+	printk("Mem-info:\n");
+	show_free_areas();
+	printk("Free swap:       %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
+	i = high_memory >> PAGE_SHIFT;
+	while (i-- > 0) {
+		total++;
+		if (mem_map[i] & MAP_PAGE_RESERVED)
+			reserved++;
+		else if (!mem_map[i])
+			free++;
+		else
+			shared += mem_map[i]-1;
+	}
+	printk("%d pages of RAM\n",total);
+	printk("%d free pages\n",free);
+	printk("%d reserved pages\n",reserved);
+	printk("%d pages shared\n",shared);
+	show_buffers();
+#ifdef CONFIG_NET
+	show_net_buffers();
+#endif
+}
+
+extern unsigned long free_area_init(unsigned long, unsigned long);
+
+/*
+ * paging_init() sets up the page tables - note that the first 4MB are
+ * already mapped by head.S.
+ *
+ * This routines also unmaps the page at virtual kernel address 0, so
+ * that we can trap those pesky NULL-reference errors in the kernel.
+ */
+unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
+{
+	unsigned long * pg_dir;
+	unsigned long * pg_table;
+	unsigned long tmp;
+	unsigned long address;
+
+	start_mem = PAGE_ALIGN(start_mem);
+	address = 0;
+	pg_dir = swapper_pg_dir;
+	while (address < end_mem) {
+		tmp = *pg_dir;
+		tmp &= PAGE_MASK;
+		if (!tmp) {
+			tmp = start_mem;
+			start_mem += PAGE_SIZE;
+		}
+		/*
+		 * also map it in at 0x00000000 for init
+		 */
+		*pg_dir = tmp | PAGE_TABLE;
+		pg_dir++;
+		pg_table = (unsigned long *) (tmp & PAGE_MASK);
+		for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) {
+			if (address < end_mem)
+				*pg_table = address | PAGE_SHARED;
+			else
+				*pg_table = 0;
+			address += PAGE_SIZE;
+		}
+	}
+#if KERNELBASE == KSEG0
+	cacheflush();
+#endif
+	invalidate();
+	return free_area_init(start_mem, end_mem);
+}
+
+void mem_init(unsigned long start_mem, unsigned long end_mem)
+{
+	int codepages = 0;
+	int reservedpages = 0;
+	int datapages = 0;
+	unsigned long tmp;
+	extern int etext;
+
+	end_mem &= PAGE_MASK;
+	high_memory = end_mem;
+
+	/* mark usable pages in the mem_map[] */
+	start_mem = PAGE_ALIGN(start_mem);
+
+	while (start_mem < high_memory) {
+		mem_map[MAP_NR(start_mem)] = 0;
+		start_mem += PAGE_SIZE;
+	}
+#ifdef CONFIG_SCSI
+	scsi_mem_init(high_memory);
+#endif
+#ifdef CONFIG_SOUND
+	sound_mem_init();
+#endif
+	for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) {
+		if (mem_map[MAP_NR(tmp)]) {
+			/*
+			 * We don't have any reserved pages on the
+			 * MIPS systems supported until now
+			 */
+			if (0)
+				reservedpages++;
+			else if (tmp < ((unsigned long) &etext - KERNELBASE))
+				codepages++;
+			else
+				datapages++;
+			continue;
+		}
+		mem_map[MAP_NR(tmp)] = 1;
+		free_page(tmp);
+	}
+	tmp = nr_free_pages << PAGE_SHIFT;
+	printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
+		tmp >> 10,
+		high_memory >> 10,
+		codepages << (PAGE_SHIFT-10),
+		reservedpages << (PAGE_SHIFT-10),
+		datapages << (PAGE_SHIFT-10));
+
+	invalidate();
+	return;
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+	int i;
+
+	i = high_memory >> PAGE_SHIFT;
+	val->totalram = 0;
+	val->sharedram = 0;
+	val->freeram = nr_free_pages << PAGE_SHIFT;
+	val->bufferram = buffermem;
+	while (i-- > 0)  {
+		if (mem_map[i] & MAP_PAGE_RESERVED)
+			continue;
+		val->totalram++;
+		if (!mem_map[i])
+			continue;
+		val->sharedram += mem_map[i]-1;
+	}
+	val->totalram <<= PAGE_SHIFT;
+	val->sharedram <<= PAGE_SHIFT;
+	return;
+}
diff -u --recursive --new-file v1.1.82/linux/arch/sparc/kernel/ioport.c linux/arch/sparc/kernel/ioport.c
--- v1.1.82/linux/arch/sparc/kernel/ioport.c	Mon Jan 16 14:18:15 1995
+++ linux/arch/sparc/kernel/ioport.c	Tue Jan 17 00:57:01 1995
@@ -1,6 +1,6 @@
 /* ioport.c:  I/O access on the Sparc. Work in progress.. Most of the things
  *            in this file are for the sole purpose of getting the kernel
- *	      throught the compiler. :-)
+ *	      through the compiler. :-)
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
diff -u --recursive --new-file v1.1.82/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.1.82/linux/drivers/block/Makefile	Mon Jan  9 07:22:00 1995
+++ linux/drivers/block/Makefile	Wed Jan 18 21:00:26 1995
@@ -34,6 +34,11 @@
 SRCS := $(SRCS) mcd.c
 endif
 
+ifdef CONFIG_AZTCD
+OBJS := $(OBJS) aztcd.o
+SRCS := $(SRCS) aztcd.c
+endif
+
 ifdef CONFIG_SBPCD
 OBJS := $(OBJS) sbpcd.o
 SRCS := $(SRCS) sbpcd.c
diff -u --recursive --new-file v1.1.82/linux/drivers/block/README.aztcd linux/drivers/block/README.aztcd
--- v1.1.82/linux/drivers/block/README.aztcd	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/README.aztcd	Wed Jan 18 21:00:26 1995
@@ -0,0 +1,688 @@
+                       Readme-File README.aztcd
+   for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 
+                          CD-ROM  Driver 
+                    BETA-Version 0.6 and newer
+                   (for other drives see 6.-8.)
+
+NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE
+      A PROPRIETARY INTERFACE (implemented on a sound card or on a
+      ISA-AT-bus card). 
+      IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE,
+      such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE
+      WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE 
+      USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER !!!
+----------------------------------------------------------------------------
+Contents of this file:
+                         1. NOTE
+                         2. INSTALLATION
+                         3. CONFIGURING YOUR KERNEL
+                         4. RECOMPILING YOUR KERNEL
+                         5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
+                         6. BUG REPORTS
+                         7. OTHER DRIVES
+                         8. IF YOU DON'T SUCCEED ... DEBUGGING  
+                         9. TECHNICAL HISTORY OF THE DRIVER
+                        10. ACKNOWLEDGEMENTS
+                        11. PROGRAMMING ADD ONS: CDPLAY.C
+                        APPENDIX: Source code of cdplay.c
+----------------------------------------------------------------------------
+
+1. NOTE 
+This software has now been tested in some different machines with AZTECH
+CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110 and has proven to be 
+pretty stable with kernel versions 1.0.0 to 1.1.75. Nevertheless I'd only
+assign BETA status to the software, as the number of known users is still 
+small. So when in 'day to day' use or when used with different firmware
+versions of the CD-ROM drives, there still may be some problems. So I'm 
+quite sure I'll see some bug reports (see chapter BUG REPORTS) and you are
+invited in helping us to increase the number of drives, which are supported,
+and in debugging this software (see chapters OTHER DRIVES and DEBUGGING).
+
+Please read the README-files carefully and always keep a backup copy of your 
+old kernel, in order to reboot if something goes wrong!
+
+
+2. INSTALLATION
+If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz 
+(xx=version number) and not included in the standard Linux kernel, read the 
+file AZTECH.CDROM.Vxx.README included in that package to install it. The 
+standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'.
+The driver consists of a header file 'aztcd.h', which normally should reside 
+in /usr/include/linux and the source code 'aztcd.c', which normally resides in
+/usr/src/linux/drivers/block. It uses /dev/aztcd0, which must be a valid block
+device with major number 29 and reside in directory /dev. To mount a CD-ROM,
+your kernel needs to have the ISO9660-filesysytem support included.
+
+
+3. CONFIGURING YOUR KERNEL
+If your kernel is already configured for using the AZTECH driver you will
+see the following message while Linux boots:
+    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
+    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>>>
+    Aztech CD-ROM Init: <drive type> detected
+    Aztech CD-ROM Init: End
+If the message looks different and you are sure to have a supported drive,
+it may have a different base address. The Aztech driver does have an 'auto-
+detection feature', which looks for the CD-ROM drive at the base address 
+specified in aztcd.h at compile time. This address can be overwritten by boot 
+parameter aztcd=....You should reboot and start Linux with boot parameter 
+aztcd=<base address>, e.g. aztcd=0x320. If you do not know the base address, 
+start your PC with DOS and look at the boot message of your CD-ROM's DOS 
+driver. 
+
+If the message looks correct, as user 'root' you should be able to mount the 
+drive by
+          mount -t iso9660 -r /dev/aztcd0 /mnt
+and use it as any other filesystem. (If this does not work, check if
+  /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
+      mknod /dev/aztcd0 b 29 0
+      mkdir /mnt                       
+
+If you still get a different message while Linux boots or when you get the 
+message, that the ISO9660-filesystem is not supported by your kernel, when
+you try to mount the CD-ROM drive, you have to recompile your kernel.
+
+
+4. RECOMPILING YOUR KERNEL
+If your kernel is not yet configured for the AZTECH driver and the ISO9660-
+filesystem, you have to recompile your kernel: 
+
+- Edit aztcd.h to set the I/O-adress to your I/O-Base adress (AZT_BASE_ADDR), 
+  the driver does not use interrupts or DMA, so if you are using an AZTECH
+  CD268, an ORCHID CD-3110 or ORCHID/WEARNES CDD110 that's the only item you 
+  have to set up. 
+  Users of other drives should read chapter OTHER DRIVES of this file.
+  You also can configure that address by LILO boot parameter aztcd=... 
+- Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support' 
+  and include the ISO9660-filesystem. 
+- Activate the new kernel, normally this is done by running lilo (don't for-
+  get to configure it before and to keep a copy of your old kernel in case
+  something goes wrong!).
+- Reboot
+- You now should see during boot some messages like
+    Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
+    Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>
+    Aztech CD-ROM Init: <drive type> detected
+    Aztech CD-ROM Init: End
+- If the message looks correct, as user 'root' you should be able to mount 
+  the drive by
+          mount -t iso9660 -r /dev/aztcd0 /mnt
+  and use it as any other filesystem. (If this does not work, check if
+  /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
+      mknod /dev/aztcd0 b 29 0
+      mkdir /mnt                       
+- If this still does not help, see chapters OTHER DRIVES and DEBUGGING.
+
+
+5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
+The driver does not support applications such as photo CD, multi session CD
+etc.. I do not plan to include the support for that in the driver, because I
+do not use such applications. If you are interested in that stuff and would
+like to extend the drivers capability on your own, please contact me, I'll
+support you as far as I can. 
+
+The drive status recognition does not work correctly in all cases. Changing
+a disk or having the door open, when a drive is already mounted, is detected 
+by the Aztech driver itself, but nevertheless causes multiple read attempts
+by the different layers of the ISO9660-filesystem driver, which finally timeout,
+so you have to wait quite a little... But isn't it bad style to change a disk 
+in a mounted drive, anyhow ?!
+
+The driver uses busy wait in most cases for the drive handshake (macros
+STEN_LOW and DTEN_LOW). I tested with a 486/DX2 at 66MHz and a Pentium at
+60MHz. Whenever you use a much faster machine you are likely to get timeout 
+messages. In that case edit aztcd.h and increase the timeout value AZT_TIMEOUT. 
+
+For some 'slow' drive commands I implemented waiting with a timer waitqueue
+(macro STEN_LOW_WAIT). If you get this timeout message, you may also edit
+aztcd.h and increase the timeout value AZT_STATUS_DELAY. The waitqueue has
+shown to be a little critical. If you get kernel panic messages, edit aztcd.c
+and substitute STEN_LOW_WAIT by STEN_LOW. Busy waiting with STEN_LOW is more
+stable, but also causes CPU overhead.
+
+
+6. BUG REPORTS
+Please send detailed bug reports and bug fixes via EMail to
+
+        zimmerma@rz.fht-esslingen.de
+
+I also would like to get positive feedback. Please inform me, if you have
+positive test results. Please include a description of your CD-ROM drive
+type and interface card, the exact firmware message during Linux bootup, 
+the version number of the AZTECH-CDROM-driver and the Linux kernel version.
+Also a description of your system's other hardware could be of interest,
+especially microprocessor type, clock frequency, other interface cards such
+as soundcards, ethernet adapter, game cards etc..
+
+I will try to collect the reports and make the necessary modifications from 
+time to time. I may also come back to you directly with some bug fixes and 
+ask you to do further testing and debugging.
+
+Editors of CD-ROMs are invited to send a 'cooperation' copy of their
+CD-ROMs to the volunteers, who provided the CD-ROM support for Linux. My
+snail mail address for such 'stuff' is
+           Prof. Dr. W. Zimmermann
+           Fachhochschule fuer Technik Esslingen
+           Fachbereich NT
+           Flandernstrasse 101
+           D-73732 Esslingen
+           Germany
+
+
+7. OTHER DRIVES
+The following drives ORCHID CDS3110, OKANO CDD110 and WEARNES nearly look 
+the same as AZTECH CDA268-01A, especially they seem to use the same command 
+codes. So it should be simple to make the AZTECH driver work with these drives. 
+
+Unfortuntately I do not have any of these drives available, so I can't test
+it. I've got reports, that it works with ORCHID CDS3110 and Game-Wave32
+sound cards and also with WEARNES CDD110 in some different combinations. In 
+some installations, it seems necessary to initialize the drive with the DOS 
+driver before (especially if combined with a sound card) and then do a warm 
+boot (CTRL-ALT-RESET) or start Linux from DOS, e.g. with 'loadlin'.
+
+Please give me positive or negative feedback, if you have tried it. Especially
+you should tell me, how the version string reads during initialization. If you
+don't succeed, read chapter DEBUGGING. Thanks in advance!
+
+Sorry for the inconvenience, but it is difficult to develop for hardware, 
+which you don't have available for testing. So if you like, please help us.
+
+
+8. DEBUGGING : IF YOU DON'T SUCCEED, TRY THE FOLLOWING
+-reread the complete README file
+-make sure, that your drive is hardware configured for 
+    transfer mode: polled
+    IRQ:           not used
+    DMA:           not used
+    Base Address:  something like 300, 320 ...
+ You can check this, when you start the DOS driver, which came with your
+ drive. By appropiately configuring the drive and the DOS driver you can
+ check, whether your drive does operate in this mode correctly under DOS. If
+ it does not operate under DOS, it won't under Linux.
+ Make sure the Base Address is configured correctly in aztcd.h, also make
+ sure, that /dev/aztcd0 exists with the correct major number (compare it with
+ the entry in file /usr/include/linux/major.h for the Aztech drive). 
+-insert a CD-ROM and close the tray
+-cold boot your PC (i.e. via the power on switch or the reset button)
+-if you start Linux via DOS, e.g. using loadlin, make sure, that the DOS
+ driver for the CD-ROM drive is not loaded (comment out the calling lines 
+ in DOS' config.sys!)
+-look for the aztcd: init message during Linux init and note them exactly
+-log in as root and do a mount -t iso9660 /dev/aztcd0 /mnt
+-if you don't succeed in the first time, try several times. Try also to open
+ and close the tray, then mount again. Please note carefully all commands
+ you typed in and the aztcd-messages, which you get.
+-if you get an 'Aztech CD-ROM init: aborted' message, read the remarks about
+ the version string below.
+
+If this does not help, do the same with the following differences 
+-start DOS before; make now sure, that the DOS driver for the CD-ROM is 
+ loaded under DOS (i.e. uncomment it again in config.sys)
+-warm boot your PC (i.e. via CTRL-ALT-DEL)
+ if you have it, you can also start via loadlin (try both).
+ ...
+ Again note all commands and the aztcd-messages.
+
+If you see STEN_LOW or STEN_LOW_WAIT error messages, increase the timeout
+values.
+
+If this still does not help, 
+-look in aztcd.c for the lines  #if 0
+                                #define AZT_TEST1
+                                ...
+                                #endif
+ and substitute '#if 0' by '#if 1'.
+-recompile your kernel and repeat the above two procedures. You will now get 
+ a bundle of debugging messages from the driver. Again note your commands
+ and the appropriate messages. If you have syslogd running, these messages
+ may also be found in syslogd's kernel log file. Nevertheless in some
+ installations syslogd does not yet run, when init() is called, thus look for
+ the aztcd-messages during init, before the login-prompt appears.
+ Then look in aztcd.c, to find out, what happened. The normal calling sequence 
+ is: aztcd_init() during Linux bootup procedure init()
+ after doing a 'mount -t iso9660 /dev/aztcd0 /mnt' the normal calling sequence is
+     aztcd_open()    -> Status 2c after cold reboot with CDROM or audio CD inserted
+                     -> Status 8  after warm reboot with CDROM inserted          
+                     -> Status 2e after cold reboot with no disk, closed tray
+                     -> Status 6e after cold reboot, mount with door open
+     aztUpdateToc()
+     aztGetDiskInfo()
+     aztGetQChannelInfo()   repeated several times
+     aztGetToc()
+     aztGetQChannelInfo()   repeated several times
+     a list of track informations
+     do_aztcd_request()  }  
+     azt_transfer()    } repeated several times
+     azt_poll          }
+ Check, if there is a difference in the calling sequence or the status flags!
+ 
+ There are a lot of other messages, eg. the ACMD-command code (defined in
+ aztcd.h), status info from the getAztStatus-command and the state sequence of
+ the finite state machine in azt_poll(). The most important are the status
+ messages, look how they are defined and try to understand, if they make
+ sense in the context where they appear. With a CD-ROM inserted the status 
+ should always be 8, except in aztcd_open(). Try to open the tray, insert a
+ audio disk, insert no disk or reinsert the CD-ROM and check, if the status
+ bits change accordingly. The status bits are the most likely point, where 
+ the drive manufacturers may implement changes.
+            
+If you still don't succeed, a good point to start is to look in aztcd.c in 
+function aztcd_init, where the drive should be detected during init. Do the
+following:
+-reboot the system with boot parameter 'aztcd=<your base address>,0x79'. With
+ parameter 0x79 most of the drive version detection is bypassed. After that 
+ you should see the complete version string including leading and trailing 
+ blanks during init. 
+ Now adapt the statement
+      if ((result[1]=='A')&&(result[2]=='Z' ...)
+ in aztcd_init() to exactly match the first 3 or 4 letters you have seen.
+-Another point is the 'smart' card detection feature in aztcd_init(). Normally
+ the CD-ROM drive is ready, when aztcd_init is trying to read the version
+ string and a time consuming ACMD_SOFT_RESET command can be avoided. This is
+ detected by looking, if AFL_OP_OK can be read correctly. If the CD-ROM drive 
+ hangs in some unknown state, e.g. because of an error before a warm start or 
+ because you first operated under DOS, even the version string may be correct, 
+ but the following commands will not. Then change the code in such a way, 
+ that the ACMD_SOFT_RESET is issued in any case, by substituting the
+ if-statement 'if ( ...=AFL_OP_OK)' by 'if (1)'.
+
+If you succeed, please mail may the exact version string of your drive and
+the code modifications, you have made together with a short explanation.
+If you don't succeed, you may mail me the output of the debugging messages.
+But remember, they are only useful, if they are exact and complete and you
+describe in detail your hardware setup and what you did (cold/warm reboot,
+with/without DOS, DOS-driver started/not started, which Linux-commands etc.)
+
+
+9. TECHNICAL HISTORY OF THE DRIVER
+The AZTECH-Driver is a rework of the Mitsumi-Driver. Four major items had to
+be reworked:
+
+a) The Mitsumi drive does issue complete status informations acknowleding
+each command, the Aztech drive does only signal that the command was
+processed. So whenever the complete status information is needed, an extra
+ACMD_GET_STATUS command is issued. The handshake procedure for the drive
+can be found in the functions aztSendCmd(), sendAztCmd() and getAztStatus().
+
+b) The Aztech Drive does not have a ACMD_GET_DISK_INFO command, so the
+necessary info about the number of tracks (firstTrack, lastTrack), disk
+length etc. has to be read from the TOC in the lead in track (see function
+aztGetDiskInfo()).
+
+c) Whenever data is read from the drive, the Mitsumi drive is started with a
+command to read an indefinite (0xffffff) number of sectors. When the appropriate 
+number of sectors is read, the drive is stopped by a ACDM_STOP command. This
+does not work with the Aztech drive. I did not find a way to stop it. The
+stop and pause commands do only work in AUDIO mode but not in DATA mode.
+Therefore I had to modify the 'finite state machine' in function azt_poll to
+only read a certain number of sectors and then start a new read on demand. As I 
+have not completely understood, how the buffer/caching scheme of the Mitsumi 
+driver was implemented, I am not sure, if I have covered all cases correctly, 
+whenever you get timeout messages, the bug is most likely to be in that
+function azt_poll() around switch(cmd) .... case ACD_S_DATA. 
+
+d) I did not get information about changing drive mode. So I doubt, that the
+code around function azt_poll() case AZT_S_MODE does work. In my test I have
+not been able to switch to reading in raw mode. For reading raw mode, Aztech
+uses a different command than for cooked mode, which I only have implemen-
+ted in the ioctl-section but not in the section which is used by the ISO9660- 
+
+The driver was developped on an AST PC with Intel 486/DX2, 8MB RAM, 340MB IDE 
+hard disk and on an AST PC with Intel Pentium 60MHz, 16MB RAM, 520MB IDE 
+running Linux kernel version 1.0.9 from the LST 1.8 Distribution. The kernel 
+was compiled with gcc.2.5.8. My CD-ROM drive is an Aztech CDA268-01A. My
+drive says, that it has Firmware Version AZT26801A1.3. It came with a ISA-bus
+interface card and works with polled I/O without DMA and without interrupts.
+The code for all other drives was 'remote' tested and debugged by a number of 
+volunteers on the Internet.
+
+Points, where I feel that possible problems might be and all points where I 
+did not completely understand the drive's behaviour or trust my own code are 
+marked with /*???*/ in the source code. There are also some parts in the 
+Mitsumi driver, where I did not completely understand their code.
+
+
+10. ACKNOWLEDGEMENTS
+Without the help of P.Bush, Aztech, who delivered technical information
+about the Aztech Drive and without the help of E.Moenkeberg, GWDG, who did a
+great job in analyzing the command structure of various CD-ROM drives, this
+work would not have been possible. E.Moenkeberg was also a great help in 
+making the software 'kernel ready' and in answering many of the CDROM-related 
+questions in the newsgroups. He really is *the* Linux CD-ROM guru. Thanks 
+also to  Ruediger Helsch, Unifix, and to all the guys on the Internet, who 
+collected valuable technical information about CDROMs. 
+
+Joe Nardone (nardone@clark.net) was a patient tester even for my first
+trial, which was more than slow, and made suggestions for code improvement.
+Especially the 'finite state machine' azt_poll() was rewritten by Joe to get
+clean C code and avoid the ugly 'gotos', which I copied from mcd.c.
+
+Robby Schirmer (schirmer@fmi.uni-passau.de) tested the audio stuff (ioctls) 
+and suggested a lot of patches for them.
+
+Joseph Piskor and Peter Nugent were the first users with the ORCHID CD3110
+and also were very patient with the problems, which occured.
+
+Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
+directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'.
+
+11. PROGRAMMING ADD ONs: cdplay.c
+You can use the ioctl-functions included in aztcd.c in your own programs. As
+an example on how to do this, you will find a tiny CD Player for audio CDs 
+named 'cdplay.c'. It allows you to play audio CDs. You can play a specified 
+track, pause and resume or skip tracks forward and backwards. If you quit the 
+program without stopping the  drive, playing is continued. You can also 
+(mis)use cdplay to read and hexdump data disks.
+'cdplay.c' can be found as a separate file in package AZTECH.CDROM.Vxx.tgz.
+If you don't have it, you can find the code in the APPENDIX of this file,
+which you should cut out with an editor and store in a separate file
+'cdplay.c'. To compile it and make it executable, do
+  gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # compiles it
+  chmod +755 /usr/local/bin/cdplay                              # makes it executable
+  ln -s /dev/aztcd0 /dev/cdrom                                  # creates a link
+   (for /usr/lib substitute the top level directory, where your include files 
+    reside,  and for /usr/local/bin the directory, where you want the executable 
+    binary to reside )
+
+You have to set the correct permissions for cdplay *and* for /dev/mcd0 or
+/dev/aztcd0 in order to use it. Remember, that you should not have /dev/cdrom 
+mounted, when you're playing audio CDs. 
+
+This program is just a hack for testing the ioctl-functions in aztcd.c, I will 
+not maintain it, so if you run into problems, discard it or have a look into 
+the source code 'cdplay.c'. The program does only contain a minimum of user 
+protection and input error detection. If you use the commands in the wrong 
+order or if you try to read a CD at wrong addresses, you may get error messages
+or even hang your machine. If you get STEN_LOW, STEN_LOW_WAIT or segment violation 
+error messages when using cdplay, after that, the system might not be stable 
+any more, so you'd better reboot. As the ioctl-functions run in kernel mode,
+most normal Linux-multitasking protection features do not work. By using
+uninitialized 'wild' pointers etc., it is easy to write to other users data and
+program areas, destroy kernel tables etc.. So if you experiment with ioctls
+as always when you are doing systems programming and kernel hacking, you
+should have a backup copy of your system in a safe place (and you also
+should try before, how to restore from a backup copy)!
+
+
+Werner Zimmermann
+Fachhochschule fuer Technik Esslingen
+(EMail: zimmerma@rz.fht-esslingen.de)
+Jan. 8, 1994
+
+---------------------------------------------------------------------------
+APPENDIX: Source code of cdplay.c
+
+/* Tiny Audio CD Player
+
+   Copyright 1994, 1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+
+This programm originally was written to test the audio functions of the
+AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before 
+using it, you should set a symlink from /dev/cdrom to your real CDROM
+device.
+
+The GNU General Public License applies to this program.
+
+History:  V0.1  W.Zimmermann: First release. Nov. 8, 1994
+          V0.2  W.Zimmermann: Enhanced functionality. Nov. 9, 1994
+          V0.3  W.Zimmermann: Additional functions. Nov. 28, 1994          
+          V0.4  W.Zimmermann: fixed some bugs. Dec. 17, 1994
+          V0.5  W.Zimmermann: clean 'scanf' commands without compiler warnings
+                              Jan. 6, 1995
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/cdrom.h>
+#include <linux/aztcd.h>
+
+void help(void)
+{ printf("Available Commands:  STOP         s      EJECT        e       QUIT         q\n");
+  printf("                     PLAY TRACK   t      PAUSE        p       RESUME       r\n");
+  printf("                     NEXT TRACK   n      REPEAT LAST  l       HELP         h\n");
+  printf("                     SUB CHANNEL  c      TRACK INFO   i       PLAY AT      a\n");
+  printf("                     READ         d      READ RAW     w                     \n");
+}
+
+int main(void)
+{ int handle;
+  unsigned char command=' ', ini=0, first=1, last=1;
+  unsigned int cmd, i,j,k, arg1,arg2,arg3;
+  struct cdrom_ti       ti;
+  struct cdrom_tochdr   tocHdr;
+  struct cdrom_subchnl  subchnl;
+  struct cdrom_tocentry entry;
+  struct cdrom_msf      msf;
+  union  { struct cdrom_msf msf;
+           unsigned char buf[2336];
+         } azt;
+
+  printf("\nMini-Audio CD-Player V0.5   (C) 1994  W.Zimmermann\n");
+  handle=open("/dev/cdrom",O_RDWR);
+  ioctl(handle,CDROMRESUME);
+  
+  if (handle<=0) 
+    { printf("Drive Error: already playing, no audio disk, door open\n");
+      printf("             or no permission (you must be ROOT in order to use this program)\n");
+    }
+  else
+    { help();
+      while (1)
+        { printf("Type command (h = help):  ");
+          scanf("%s",&command); 
+          switch (command)
+            { case 'e':   cmd=CDROMEJECT;
+                          ioctl(handle,cmd);
+                          break;
+              case 'p':   if (!ini)
+                             { printf("Command not allowed - play track first\n");
+                             }
+                          else
+                             { cmd=CDROMPAUSE;
+                               if (ioctl(handle,cmd)) printf("Drive Error\n");
+                             }
+                          break;
+              case 'r':   if (!ini)
+                             { printf("Command not allowed - play track first\n");
+                             }
+                          else
+                             { cmd=CDROMRESUME;
+                               if (ioctl(handle,cmd)) printf("Drive Error\n");
+                             }
+                          break;
+              case 's':   cmd=CDROMPAUSE;
+                          if (ioctl(handle,cmd)) printf("Drive error or already stopped\n");
+                          ini=0;
+                          cmd=CDROMSTOP;
+                          if (ioctl(handle,cmd)) printf("Drive error\n");
+                          ini=0;
+                          break;
+              case 't':   cmd=CDROMREADTOCHDR;
+                          if (ioctl(handle,cmd,&tocHdr)) printf("Drive Error\n");
+                          first=tocHdr.cdth_trk0;
+                          last= tocHdr.cdth_trk1;
+                          if ((first==0)||(first>last))
+                            { printf ("--could not read TOC\n");
+                            }
+                          else
+                            { printf("--first track: %d   --last track: %d   --enter track number: ",first,last);
+                              cmd=CDROMPLAYTRKIND;
+                              scanf("%i",&arg1);
+                              ti.cdti_trk0=arg1;
+                              if (ti.cdti_trk0<first) ti.cdti_trk0=first;
+                              if (ti.cdti_trk0>last)  ti.cdti_trk0=last;
+                              ti.cdti_ind0=0;
+                              ti.cdti_trk1=last;
+                              ti.cdti_ind1=0;
+                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+                              ini=1;
+                            } 
+                          break;
+              case 'n':   if (!ini++) 
+                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
+                              first=tocHdr.cdth_trk0;
+                              last= tocHdr.cdth_trk1;
+                              ti.cdti_trk0=first-1;
+                            }
+                          if ((first==0)||(first>last))
+                            { printf ("--could not read TOC\n");
+                            }
+                          else
+                            { cmd=CDROMPLAYTRKIND;
+                              if (++ti.cdti_trk0 > last)  ti.cdti_trk0=last;
+                              ti.cdti_ind0=0;
+                              ti.cdti_trk1=last;
+                              ti.cdti_ind1=0;
+                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+                              ini=1;
+                            }
+                          break;
+              case 'l':   if (!ini++)
+                            { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
+                              first=tocHdr.cdth_trk0;
+                              last= tocHdr.cdth_trk1;
+                              ti.cdti_trk0=first+1;
+                            }
+                          if ((first==0)||(first>last))
+                            { printf ("--could not read TOC\n");
+                            }
+                          else
+                            { cmd=CDROMPLAYTRKIND;
+                              if (--ti.cdti_trk0 < first) ti.cdti_trk0=first;
+                              ti.cdti_ind0=0;
+                              ti.cdti_trk1=last;
+                              ti.cdti_ind1=0;
+                              if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+                              ini=1;
+                            }  
+                          break;
+              case 'c':   subchnl.cdsc_format=CDROM_MSF;
+                          if (ioctl(handle,CDROMSUBCHNL,&subchnl)) 
+                            printf("Drive Error\n");
+                          else
+                            { printf("AudioStatus:%s   Track:%d  Mode:%d   MSF=%d:%d:%d\n", \
+                              subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",\
+                              subchnl.cdsc_trk,subchnl.cdsc_adr, \
+                              subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, \
+                              subchnl.cdsc_absaddr.msf.frame);
+                            }
+                          break;              
+              case 'i':   if (!ini)
+                            { printf("Command not allowed - play track first\n");
+                            }
+                          else
+                            { cmd=CDROMREADTOCENTRY;
+                              printf("Track No.: ");
+                              scanf("%d",&arg1);
+                              entry.cdte_track=arg1;
+                              if (entry.cdte_track<first) entry.cdte_track=first;
+                              if (entry.cdte_track>last)  entry.cdte_track=last;
+                              entry.cdte_format=CDROM_MSF;
+                              if (ioctl(handle,cmd,&entry)) 
+                               { printf("Drive error or invalid track no.\n");
+                               }
+                              else
+                               { printf("Mode %d Track, starts at %d:%d:%d\n", \
+                               entry.cdte_adr,entry.cdte_addr.msf.minute, \
+                               entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame);
+                               }
+                            }
+                          break;
+              case 'a':   cmd=CDROMPLAYMSF;
+                          printf("Adress (min:sec:frame)  ");
+                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+                          msf.cdmsf_min0  =arg1;
+                          msf.cdmsf_sec0  =arg2;
+                          msf.cdmsf_frame0=arg3;
+                          if (msf.cdmsf_sec0  > 59) msf.cdmsf_sec0  =59;
+                          if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74;
+                          msf.cdmsf_min1=60;
+                          msf.cdmsf_sec1=00;
+                          msf.cdmsf_frame1=00;
+                          if (ioctl(handle,cmd,&msf)) 
+                           { printf("Drive error or invalid adress\n");
+                           }
+                          break;
+#ifdef AZT_PRIVATE_IOCTLS
+              case 'd':   cmd=CDROMREADMODE1;
+                          printf("Adress (min:sec:frame)  ");
+                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+                          azt.msf.cdmsf_min0  =arg1;
+                          azt.msf.cdmsf_sec0  =arg2;
+                          azt.msf.cdmsf_frame0=arg3;
+                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
+                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
+                          if (ioctl(handle,cmd,&azt.msf)) 
+                           { printf("Drive error or invalid adress\n");
+                           }
+                          k=0;
+                          getchar();
+                          for (i=0;i<128;i++)
+                           { printf("%4d:",i*16);
+                             for (j=0;j<16;j++)
+                               { printf("%2x ",azt.buf[i*16+j]);
+                               }
+                             for (j=0;j<16;j++)
+                               { if (isalnum(azt.buf[i*16+j])) 
+                                   printf("%c",azt.buf[i*16+j]);
+                                 else
+                                   printf(".");
+                               }
+                             printf("\n"); 
+                             k++;
+                             if (k>=20)
+                              { printf("press ENTER to continue\n");
+                                getchar();
+                                k=0;
+                              }
+                           } 
+                          break;
+              case 'w':   cmd=CDROMREADMODE2;
+                          printf("Adress (min:sec:frame)  ");
+                          scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+                          azt.msf.cdmsf_min0  =arg1;
+                          azt.msf.cdmsf_sec0  =arg2;
+                          azt.msf.cdmsf_frame0=arg3;                          
+                          if (azt.msf.cdmsf_sec0  > 59) azt.msf.cdmsf_sec0  =59;
+                          if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
+                          if (ioctl(handle,cmd,&azt)) 
+                           { printf("Drive error or invalid adress\n");
+                           }
+                          k=0;
+                          for (i=0;i<146;i++)
+                           { printf("%4d:",i*16);
+                             for (j=0;j<16;j++)
+                               { printf("%2x ",azt.buf[i*16+j]);
+                               }
+                             for (j=0;j<16;j++)
+                               { if (isalnum(azt.buf[i*16+j])) 
+                                   printf("%c",azt.buf[i*16+j]);
+                                 else
+                                   printf(".");
+                               }
+                             printf("\n"); 
+                             k++;
+                             if (k>=20)
+                              { getchar();
+                                k=0;
+                              }
+                           } 
+                          break;
+#endif
+              case 'q':   if (close(handle)) printf("Drive Error: CLOSE\n");
+                          exit(0);
+              case 'h':   help();
+                          break;
+              default:    printf("unknown command\n");
+                          break;
+            }
+       }
+    }
+  return 0;
+}
+
diff -u --recursive --new-file v1.1.82/linux/drivers/block/aztcd.c linux/drivers/block/aztcd.c
--- v1.1.82/linux/drivers/block/aztcd.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/aztcd.c	Wed Jan 18 21:17:50 1995
@@ -0,0 +1,1580 @@
+#define AZT_VERSION "V0.72"
+/*      $Id: aztcd.c,v 0.72 1995/01/13 15:21:09 root Exp root $
+	linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
+
+	Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+
+	based on Mitsumi CDROM driver by  Martin Hariss and preworks by
+	Eberhard Moenkeberg.
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2, or (at your option)
+	any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	HISTORY
+	V0.0    Adaption to Adaptec CD268-01A Version 1.3
+		Version is PRE_ALPHA, unresolved points:
+		1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW
+		   thus driver causes CPU overhead and is very slow 
+		2. could not find a way to stop the drive, when it is
+		   in data read mode, therefore I had to set
+		   msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one
+		   frame can be read in sequence, this is also the reason for
+		3. getting 'timeout in state 4' messages, but nevertheless
+		   it works
+		W.Zimmermann, Okt. 31, 1994
+	V0.1    Version is ALPHA, problems #2 and #3 resolved.  
+		W.Zimmermann, Nov. 3, 1994
+	V0.2    Modification to some comments, debugging aids for partial test
+		with Borland C under DOS eliminated. Timer interrupt wait 
+		STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented; 
+		use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_
+		SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy 
+		waiting seems better to me than interrupt rescheduling.
+		Besides that, when used in the wrong place, STEN_LOW_WAIT causes
+		kernel panic.
+		In function aztPlay command ACMD_PLAY_AUDIO added, should make
+		audio functions work. The Aztech drive needs different commands
+		to read data tracks and play audio tracks.
+		W.Zimmermann, Nov. 8, 1994
+	V0.3    Recognition of missing drive during boot up improved (speeded up).
+		W.Zimmermann, Nov. 13, 1994
+	V0.35   Rewrote the control mechanism in azt_poll (formerly mcd_poll) 
+		including removal of all 'goto' commands. :-); 
+		J. Nardone, Nov. 14, 1994
+	V0.4    Renamed variables and constants to 'azt' instead of 'mcd'; had
+		to make some "compatibility" defines in azt.h; please note,
+		that the source file was renamed to azt.c, the include file to
+		azt.h                
+		Speeded up drive recognition during init (will be a little bit 
+		slower than before if no drive is installed!); suggested by
+		Robby Schirmer.
+		read_count declared volatile and set to AZT_BUF_SIZ to make
+		drive faster (now 300kB/sec, was 60kB/sec before, measured
+		by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096';
+		different AZT_BUF_SIZes were test, above 16 no further im-
+		provement seems to be possible; suggested by E.Moenkeberg.
+		W.Zimmermann, Nov. 18, 1994
+	V0.42   Included getAztStatus command in GetQChannelInfo() to allow
+		reading Q-channel info on audio disks, if drive is stopped, 
+		and some other bug fixes in the audio stuff, suggestet by 
+		Robby Schirmer.
+		Added more ioctls (reading data in mode 1 and mode 2).
+		Completely removed the old azt_poll() routine.
+		Detection of ORCHID CDS-3110 in aztcd_init implemented.
+		Additional debugging aids (see the readme file).
+		W.Zimmermann, Dez. 9, 1994  
+	V0.50   Autodetection of drives inplemented.
+		W.Zimmermann, Dez. 12, 1994
+	V0.52   Prepared for including in the standard kernel, renamed most
+		variables to contain 'azt', included autoconf.h
+		W.Zimmermann, Dez. 16, 1994        
+	V0.6    Version for being included in the standard Linux kernel.
+		Renamed source and header file to aztcd.c and aztcd.h
+		W.Zimmermann, Dez. 24, 1994
+	V0.7    Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case
+		CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl,
+		which causes kernel crashs, when playing audio, changed 
+		include-files (config.h instead of autoconf.h, removed
+		delay.h)
+		W.Zimmermann, Jan. 8, 1995
+	V0.72   Some more modifications for adaption to the standard kernel.
+		W.Zimmermann, Jan. 16, 1995
+			     
+	NOTE: 
+	Points marked with ??? are questionable !
+*/
+#include <linux/major.h>
+#include <linux/config.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#ifdef CONFIG_AZTCD
+#define MAJOR_NR AZTECH_CDROM_MAJOR 
+#else
+#define MAJOR_NR MITSUMI_CDROM_MAJOR    /*use Mitsumi major number, if Aztech*/                 
+#endif                                  /*major number is not configured*/
+
+#include "blk.h"
+#include <linux/aztcd.h>
+
+static int aztPresent = 0;
+
+#if 0
+#define AZT_TEST1 /* <int-..> */
+#define AZT_TEST2 /* do_aztcd_request */
+#define AZT_TEST3 /* AZT_S_state */
+#define AZT_TEST4 /* QUICK_LOOP-counter */
+#define AZT_TEST5 /* port(1) state */
+#define AZT_DEBUG
+#define AZT_PRIVATE_IOCTLS /*incompatible ioctls*/
+#endif
+
+#define CURRENT_VALID \
+  (CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR && CURRENT -> cmd == READ \
+   && CURRENT -> sector != -1)
+
+#define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
+#define AZT_BUF_SIZ 16
+
+static volatile int azt_transfer_is_active=0;
+
+static char azt_buf[2048*AZT_BUF_SIZ];  /*buffer for block size conversion*/
+#ifdef AZT_PRIVATE_IOCTLS
+static char buf[2336];                  /*separate buffer for the ioctls*/
+#endif
+
+static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn;
+static volatile int azt_buf_in, azt_buf_out = -1;
+static volatile int azt_error=0;
+static int azt_open_count=0;
+enum azt_state_e {
+  AZT_S_IDLE,    /* 0 */
+  AZT_S_START,   /* 1 */
+  AZT_S_MODE,    /* 2 */
+  AZT_S_READ,    /* 3 */
+  AZT_S_DATA,    /* 4 */
+  AZT_S_STOP,    /* 5 */
+  AZT_S_STOPPING /* 6 */
+};
+static volatile enum azt_state_e azt_state = AZT_S_IDLE;
+#ifdef AZT_TEST3
+static volatile enum azt_state_e azt_state_old = AZT_S_STOP;  
+static volatile int azt_st_old = 0;
+#endif
+
+static int azt_mode = -1;
+static int ACMD_DATA_READ= ACMD_PLAY_READ;
+static volatile int azt_read_count = 1;
+
+#define READ_TIMEOUT 3000
+
+static short azt_port = AZT_BASE_ADDR;
+static char  azt_cont = 0;
+static char  azt_init_end = 0;
+
+static int AztTimeout, AztTries;
+static struct wait_queue *azt_waitq = NULL; 
+
+static struct azt_DiskInfo DiskInfo;
+static struct azt_Toc Toc[MAX_TRACKS];
+static struct azt_Play_msf azt_Play;
+
+static int  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+static char aztDiskChanged = 1;
+static char aztTocUpToDate = 0;
+
+
+static void azt_transfer(void);
+static void azt_poll(void);
+static void azt_invalidate_buffers(void);
+static void do_aztcd_request(void);
+static void azt_hsg2msf(long hsg, struct msf *msf);
+static void azt_bin2bcd(unsigned char *p);
+static int  azt_bcd2bin(unsigned char bcd);
+static int  aztStatus(void);
+static int  getAztStatus(void);
+static int  aztSendCmd(int cmd);
+static int  sendAztCmd(int cmd, struct azt_Play_msf *params);
+static int  aztGetQChannelInfo(struct azt_Toc *qp);
+static int  aztUpdateToc(void);
+static int  aztGetDiskInfo(void);
+static int  aztGetToc(void);
+static int  aztGetValue(unsigned char *result);
+static void aztStatTimer(void);
+
+static unsigned char aztIndatum;
+static unsigned long aztTimeOutCount;
+
+/* Macros for the drive hardware interface handshake, these macros use
+   busy waiting */
+/* Wait for OP_OK = drive answers with AFL_OP_OK after recieving a command*/
+# define OP_OK op_ok()
+void op_ok(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(DATA_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ printk("aztcd: Error Wait OP_OK\n");
+	  break;
+	}
+     } while (aztIndatum!=AFL_OP_OK);
+}
+
+/* Wait for PA_OK = drive answers with AFL_PA_OK after recieving parameters*/
+# define PA_OK pa_ok()
+void pa_ok(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(DATA_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ printk("aztcd: Error Wait PA_OK\n");
+	  break;
+	}
+     } while (aztIndatum!=AFL_PA_OK);
+}
+     
+/* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/
+# define STEN_LOW  sten_low()
+void sten_low(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(STATUS_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ if (azt_init_end) printk("aztcd: Error Wait STEN_LOW\n");
+	  break;
+	}
+     } while (aztIndatum&AFL_STATUS);
+}
+
+/* Wait for DTEN=Low = handshake signal 'Data available'*/
+# define DTEN_LOW dten_low()
+void dten_low(void)
+{ aztTimeOutCount=0; 
+  do { aztIndatum=inb(STATUS_PORT);
+       aztTimeOutCount++;
+       if (aztTimeOutCount>=AZT_TIMEOUT)
+	{ printk("aztcd: Error Wait DTEN_OK\n");
+	  break;
+	}
+     } while (aztIndatum&AFL_DATA);
+}
+
+/* 
+ * Macro for timer wait on STEN=Low, should only be used for 'slow' commands;
+ * may cause kernel panic when used in the wrong place
+*/
+#define STEN_LOW_WAIT   statusAzt()
+void statusAzt(void)
+{ AztTimeout = AZT_STATUS_DELAY;
+  SET_TIMER(aztStatTimer, 1); 
+  sleep_on(&azt_waitq);    
+  if (AztTimeout <= 0) printk("aztcd: Error Wait STEN_LOW_WAIT\n");
+  return;
+}
+
+static void aztStatTimer(void)
+{       if (!(inb(STATUS_PORT) & AFL_STATUS))
+	{       wake_up(&azt_waitq);
+		return;
+	}
+	AztTimeout--;
+	if (AztTimeout <= 0)
+	{       wake_up(&azt_waitq);
+		return;
+	}
+	SET_TIMER(aztStatTimer, 1);
+}
+
+
+void aztcd_setup(char *str, int *ints)
+{  if (ints[0] > 0)
+      azt_port = ints[1];
+   if (ints[0] > 1)
+      azt_cont = ints[2];
+}
+
+/* 
+ * Send a single command, return -1 on error, else 0
+*/
+static int aztSendCmd(int cmd)
+{  unsigned char data;
+   int retry;
+
+#ifdef AZT_DEBUG
+   printk("aztcd: Executing command %x\n",cmd);
+#endif
+   outb(POLLED,MODE_PORT);
+   do { if (inb(STATUS_PORT)&AFL_STATUS) break;
+	inb(DATA_PORT);    /* if status left from last command, read and */
+      } while (1);         /* discard it */
+   do { if (inb(STATUS_PORT)&AFL_DATA) break;
+	inb(DATA_PORT);    /* if data left from last command, read and */
+      } while (1);         /* discard it */
+   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+     { outb((unsigned char) cmd,CMD_PORT);
+       STEN_LOW;
+       data=inb(DATA_PORT);
+       if (data==AFL_OP_OK)
+	 { return 0;}           /*OP_OK?*/
+       if (data==AFL_OP_ERR)
+	 { STEN_LOW;
+	   data=inb(DATA_PORT);
+	   printk("### Error 1 aztcd: aztSendCmd %x  Error Code %x\n",cmd,data);
+	 }
+     }
+   if (retry>=AZT_RETRY_ATTEMPTS)
+     { printk("### Error 2 aztcd: aztSendCmd %x \n",cmd);
+       azt_error=0xA5;
+     }
+   return -1;
+}
+
+/*
+ * Send a play or read command to the drive, return -1 on error, else 0
+*/
+static int sendAztCmd(int cmd, struct azt_Play_msf *params)
+{  unsigned char data;
+   int retry;
+
+#ifdef AZT_DEBUG
+   printk("start=%02x:%02x:%02x  end=%02x:%02x:%02x\n", \
+	   params->start.min, params->start.sec, params->start.frame, \
+	   params->end.min,   params->end.sec,   params->end.frame);
+#endif   
+   for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+     { aztSendCmd(cmd);
+       outb(params -> start.min,CMD_PORT);
+       outb(params -> start.sec,CMD_PORT);
+       outb(params -> start.frame,CMD_PORT);
+       outb(params -> end.min,CMD_PORT);
+       outb(params -> end.sec,CMD_PORT);
+       outb(params -> end.frame,CMD_PORT);
+       STEN_LOW;
+       data=inb(DATA_PORT);
+       if (data==AFL_PA_OK)
+	 { return 0;}           /*PA_OK ?*/
+       if (data==AFL_PA_ERR)
+	 { STEN_LOW;
+	   data=inb(DATA_PORT);
+	   printk("### Error 1 aztcd: sendAztCmd %x  Error Code %x\n",cmd,data);
+	 }
+     }
+   if (retry>=AZT_RETRY_ATTEMPTS)
+     { printk("### Error 2 aztcd: sendAztCmd %x\n ",cmd);
+       azt_error=0xA5;
+     }
+   return -1;
+}
+
+
+/* 
+ * Checking if the media has been changed not yet implemented
+*/
+static int check_aztcd_media_change(dev_t full_dev)
+{ return 0;
+}
+
+
+/* used in azt_poll to poll the status, expects another programm to issue a 
+ * ACMD_GET_STATUS directly before 
+ */
+static int aztStatus(void)  
+{       int st;
+	int i;
+
+	i = inb(STATUS_PORT) & AFL_STATUS;   /* is STEN=0?    ???*/
+	if (!i)
+	{
+		st = inb(DATA_PORT) & 0xFF;
+		return st;
+	}
+	else
+		return -1;
+}
+
+/*
+ * Get the drive status
+ */
+static int getAztStatus(void)
+{       int st;
+
+	if (aztSendCmd(ACMD_GET_STATUS)) return -1;
+	STEN_LOW;
+	st = inb(DATA_PORT) & 0xFF;
+#ifdef AZT_DEBUG
+	printk("aztcd: Status = %x\n",st);
+#endif
+	if ((st == 0xFF)||(st&AST_CMD_CHECK))
+	 { printk("aztcd: AST_CMD_CHECK error or no status available\n");
+	   return -1;
+	 }
+
+	if (((st&AST_MODE_BITS)!=AST_BUSY) && (aztAudioStatus == CDROM_AUDIO_PLAY))
+		/* XXX might be an error? look at q-channel? */
+		aztAudioStatus = CDROM_AUDIO_COMPLETED;
+
+	if (st & AST_DSK_CHG)
+	{
+		aztDiskChanged = 1;
+		aztTocUpToDate = 0;
+		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+	}
+	return st;
+}
+
+
+/*
+ * Send a 'Play' command and get the status.  Use only from the top half.
+ */
+static int aztPlay(struct azt_Play_msf *arg)
+{       if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) return -1;
+	return 0;
+}
+
+
+long azt_msf2hsg(struct msf *mp)
+{
+#ifdef AZT_DEBUG
+	if (mp->min  >=70) printk("aztcd: Error msf2hsg address Minutes\n");
+	if (mp->sec  >=60) printk("aztcd: Error msf2hsg address Seconds\n");
+	if (mp->frame>=75) printk("aztcd: Error msf2hsg address Frames\n");
+#endif
+	return azt_bcd2bin(mp -> frame)
+		+ azt_bcd2bin(mp -> sec) * 75
+		+ azt_bcd2bin(mp -> min) * 4500
+		- 150;
+}
+
+static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{       int i, st;
+	struct azt_Toc qInfo;
+	struct cdrom_ti ti;
+	struct cdrom_tochdr tocHdr;
+	struct cdrom_msf msf;
+	struct cdrom_tocentry entry;
+	struct azt_Toc *tocPtr;            
+	struct cdrom_subchnl subchnl;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztcd_ioctl - Command:%x\n",cmd);
+#endif
+	if (!ip) return -EINVAL;
+	if (getAztStatus()<0) return -EIO;
+	if (!aztTocUpToDate)
+	{ if ((i=aztUpdateToc())<0) return i; /* error reading TOC */
+	}
+
+	switch (cmd)
+	{
+	case CDROMSTART:     /* Spin up the drive */
+		/* Don't think we can do this.  Even if we could,
+		 * I think the drive times out and stops after a while
+		 * anyway.  For now, ignore it.
+		 */
+		break;
+	case CDROMSTOP:      /* Spin down the drive */
+		if (aztSendCmd(ACMD_STOP)) return -1;
+		STEN_LOW_WAIT;
+		/* should we do anything if it fails? */
+		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMPAUSE:     /* Pause the drive */
+/*              if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL; 
+*/
+		if (aztGetQChannelInfo(&qInfo) < 0)
+		{ /* didn't get q channel info */
+		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		  return 0;
+		}
+		azt_Play.start = qInfo.diskTime;        /* remember restart point */
+		if (aztSendCmd(ACMD_PAUSE)) return -1;
+		STEN_LOW_WAIT;
+		aztAudioStatus = CDROM_AUDIO_PAUSED;
+		break;
+	case CDROMRESUME:    /* Play it again, Sam */
+		if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL;
+		/* restart the drive at the saved position. */
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
+		if (st) return st;
+		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+		if (ti.cdti_trk0 < DiskInfo.first
+			|| ti.cdti_trk0 > DiskInfo.last
+			|| ti.cdti_trk1 < ti.cdti_trk0)
+		{ return -EINVAL;
+		}
+		if (ti.cdti_trk1 > DiskInfo.last)
+		   ti. cdti_trk1 = DiskInfo.last;
+		azt_Play.start = Toc[ti.cdti_trk0].diskTime;
+		azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+	azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+	azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
+/*              if (aztAudioStatus == CDROM_AUDIO_PLAY) 
+		{ if (aztSendCmd(ACMD_STOP)) return -1;
+		  STEN_LOW;
+		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		}
+*/
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+		if (st) return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		/* convert to bcd */
+		azt_bin2bcd(&msf.cdmsf_min0);
+		azt_bin2bcd(&msf.cdmsf_sec0);
+		azt_bin2bcd(&msf.cdmsf_frame0);
+		azt_bin2bcd(&msf.cdmsf_min1);
+		azt_bin2bcd(&msf.cdmsf_sec1);
+		azt_bin2bcd(&msf.cdmsf_frame1);
+		azt_Play.start.min = msf.cdmsf_min0;
+		azt_Play.start.sec = msf.cdmsf_sec0;
+		azt_Play.start.frame = msf.cdmsf_frame0;
+		azt_Play.end.min = msf.cdmsf_min1;
+		azt_Play.end.sec = msf.cdmsf_sec1;
+		azt_Play.end.frame = msf.cdmsf_frame1;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+
+	case CDROMREADTOCHDR:        /* Read the table of contents header */
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
+		if (st) return st;
+		tocHdr.cdth_trk0 = DiskInfo.first;
+		tocHdr.cdth_trk1 = DiskInfo.last;
+		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+		break;
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof entry);
+		if (st) return st;
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
+		if (st) return st;
+		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+		if (!aztTocUpToDate) aztGetDiskInfo();
+		if (entry.cdte_track == CDROM_LEADOUT)
+		  tocPtr = &Toc[DiskInfo.last + 1];   /* ??? */
+		else if (entry.cdte_track > DiskInfo.last
+				|| entry.cdte_track < DiskInfo.first)
+		{ return -EINVAL;
+		}
+		else 
+		  tocPtr = &Toc[entry.cdte_track];
+		entry.cdte_adr = tocPtr -> ctrl_addr;
+		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+		if (entry.cdte_format == CDROM_LBA)
+		  entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime);
+		else if (entry.cdte_format == CDROM_MSF)
+		{ entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min);
+		  entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec);
+		  entry.cdte_addr.msf.frame  = azt_bcd2bin(tocPtr -> diskTime.frame);
+		}
+		else
+		{ return -EINVAL;
+		}
+		memcpy_tofs((void *) arg, &entry, sizeof entry);
+		break;
+	case CDROMSUBCHNL:   /* Get subchannel info */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof subchnl);
+		if (st) return st;
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
+		if (st) return st;
+		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
+		if (aztGetQChannelInfo(&qInfo) < 0)
+		  return -EIO;
+		subchnl.cdsc_audiostatus = aztAudioStatus;
+		subchnl.cdsc_adr = qInfo.ctrl_addr;
+		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
+		subchnl.cdsc_trk = azt_bcd2bin(qInfo.track);
+		subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex);
+		if (subchnl.cdsc_format == CDROM_LBA)
+		{ subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime);
+		  subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime);
+		}
+		else if (subchnl.cdsc_format == CDROM_MSF)
+		{ subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min);
+		  subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec);
+		  subchnl.cdsc_absaddr.msf.frame = azt_bcd2bin(qInfo.diskTime.frame);
+		  subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min);
+		  subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec);
+		  subchnl.cdsc_reladdr.msf.frame  = azt_bcd2bin(qInfo.trackTime.frame);
+		}
+		else
+		  return -EINVAL;
+		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
+		break;
+	case CDROMVOLCTRL:   /* Volume control */
+	/*
+	 * This is not working yet.  Setting the volume by itself does
+	 * nothing.  Following the 'set' by a 'play' results in zero
+	 * volume.  Something to work on for the next release.
+	 */
+		break;
+	case CDROMEJECT:
+	       /* all drives can atleast stop! */
+		if (aztAudioStatus == CDROM_AUDIO_PLAY) 
+		{ if (aztSendCmd(ACMD_STOP)) return -1;
+		  STEN_LOW_WAIT;
+		}
+		if (aztSendCmd(ACMD_EJECT)) return -1;
+		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMREADMODE1: /*read data in mode 1 (2048 Bytes)*/
+	case CDROMREADMODE2: /*read data in mode 2 (2336 Bytes)*/
+/*Take care, the following code is not compatible with other CD-ROM drivers,
+  use it at your own risk with cdplay.c. Normally it is not activated, as 
+  AZT_PRIVATE_IOCTLS is not defined
+*/                  
+#ifdef AZT_PRIVATE_IOCTLS 
+		{ st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
+		  if (st) return st;
+		  st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf);
+		  if (st) return st;
+		  memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		  /* convert to bcd */
+		  azt_bin2bcd(&msf.cdmsf_min0);
+		  azt_bin2bcd(&msf.cdmsf_sec0);
+		  azt_bin2bcd(&msf.cdmsf_frame0);
+		  msf.cdmsf_min1=0;
+		  msf.cdmsf_sec1=0;
+		  msf.cdmsf_frame1=1; /*read only one frame*/
+		  azt_Play.start.min = msf.cdmsf_min0;
+		  azt_Play.start.sec = msf.cdmsf_sec0;
+		  azt_Play.start.frame = msf.cdmsf_frame0;
+		  azt_Play.end.min = msf.cdmsf_min1;
+		  azt_Play.end.sec = msf.cdmsf_sec1;
+		  azt_Play.end.frame = msf.cdmsf_frame1;
+		  if (cmd==CDROMREADMODE1)
+		  { sendAztCmd(ACMD_DATA_READ, &azt_Play);
+		    DTEN_LOW;
+		    insb(DATA_PORT,buf,2048);
+		    memcpy_tofs((void *) arg, &buf, 2048);
+		  }
+		  else /*CDROMREADMODE2*/
+		  { sendAztCmd(ACMD_DATA_READ_RAW, &azt_Play);
+		    DTEN_LOW;
+		    insb(DATA_PORT,buf,2336);
+		    memcpy_tofs((void *) arg, &buf, 2336);
+		  }
+		 } 
+#endif  /*end of incompatible code*/               
+		break;
+	default:
+		return -EINVAL;
+	}
+#ifdef AZT_DEBUG
+	printk("aztcd: exiting aztcd_ioctl\n");
+#endif
+	return 0;
+}
+
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+static void azt_transfer(void)
+{ 
+#ifdef AZT_TEST
+  printk("aztcd: executing azt_transfer\n");
+#endif
+  if (CURRENT_VALID) {
+    while (CURRENT -> nr_sectors) {
+      int bn = CURRENT -> sector / 4;
+      int i;
+      for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i)
+	;
+      if (i < AZT_BUF_SIZ) {
+	int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+	int nr_sectors = 4 - (CURRENT -> sector & 3);
+	if (azt_buf_out != i) {
+	  azt_buf_out = i;
+	  if (azt_buf_bn[i] != bn) {
+	    azt_buf_out = -1;
+	    continue;
+	  }
+	}
+	if (nr_sectors > CURRENT -> nr_sectors)
+	  nr_sectors = CURRENT -> nr_sectors;
+	memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512);
+	CURRENT -> nr_sectors -= nr_sectors;
+	CURRENT -> sector += nr_sectors;
+	CURRENT -> buffer += nr_sectors * 512;
+      } else {
+	azt_buf_out = -1;
+	break;
+      }
+    }
+  }
+}
+
+
+static void do_aztcd_request(void)
+{
+#ifdef AZT_TEST
+  printk(" do_aztcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+  azt_transfer_is_active = 1;
+  while (CURRENT_VALID) {
+    if (CURRENT->bh) {
+      if (!CURRENT->bh->b_lock)
+	panic(DEVICE_NAME ": block not locked");
+    }
+    azt_transfer();
+    if (CURRENT -> nr_sectors == 0) {
+      end_request(1);
+    } else {
+      azt_buf_out = -1;         /* Want to read a block not in buffer */
+      if (azt_state == AZT_S_IDLE) {
+	if (!aztTocUpToDate) {
+	  if (aztUpdateToc() < 0) {
+	    while (CURRENT_VALID)
+	      end_request(0);
+	    break;
+	  }
+	}
+	azt_state = AZT_S_START;
+	AztTries = 5;
+	SET_TIMER(azt_poll, 1);
+      }
+      break;
+    }
+  }
+  azt_transfer_is_active = 0;
+#ifdef AZT_TEST2
+  printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
+	  azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+  printk(" do_aztcd_request ends\n");
+#endif
+
+}
+
+static void azt_poll(void)
+{
+    int st = 0;
+    int loop_ctl = 1;
+    int skip = 0;
+
+    if (azt_error) {                             /* ???*/
+	if (aztSendCmd(ACMD_GET_ERROR)) return;
+	STEN_LOW;
+	azt_error=inb(DATA_PORT)&0xFF;
+	printk("aztcd: I/O error 0x%02x\n", azt_error);
+	azt_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+	if (AztTries == 5)
+	  printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn);
+#endif
+	if (!AztTries--) {
+	  printk("aztcd: read of block %d failed, maybe audio disk? Giving up\n", azt_next_bn);
+	  if (azt_transfer_is_active) {
+	    AztTries = 0;
+	    loop_ctl = 0;
+	  }
+	  if (CURRENT_VALID)
+	    end_request(0);
+	  AztTries = 5;
+	}
+    azt_error = 0;
+    azt_state = AZT_S_STOP;
+    }
+
+    while (loop_ctl)
+    {
+      loop_ctl = 0;   /* each case must flip this back to 1 if we want
+			 to come back up here */
+      switch (azt_state) {
+
+	case AZT_S_IDLE:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_IDLE\n");
+	    }
+#endif
+	  return;
+
+	case AZT_S_START:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_START\n");
+	  }
+#endif
+
+	  if(aztSendCmd(ACMD_GET_STATUS)) return;  /*result will be checked by aztStatus() */
+	  azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE;
+	  AztTimeout = 3000;
+	  break;
+
+	case AZT_S_MODE:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_MODE\n");
+	  }
+#endif
+	  if (!skip) {
+	    if ((st = aztStatus()) != -1) {
+	      if (st & AST_DSK_CHG) {
+		aztDiskChanged = 1;
+		aztTocUpToDate = 0;
+		azt_invalidate_buffers();
+	      }
+	    } else break;
+	  }
+	  skip = 0;
+
+	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+	    if (azt_transfer_is_active) {
+	      azt_state = AZT_S_START;
+	      loop_ctl = 1;   /* goto immediately */
+	      break;
+	    }
+	    azt_state = AZT_S_IDLE;
+	    while (CURRENT_VALID)
+	      end_request(0);
+	    return;
+	  }
+					/*???*/
+	  if (aztSendCmd(ACMD_SET_MODE)) return;
+	  outb(0x01, DATA_PORT);            /*Mode 1*/
+	  PA_OK;
+	  STEN_LOW;
+	  if (aztSendCmd(ACMD_GET_STATUS)) return;
+	  azt_mode = 1;
+	  azt_state = AZT_S_READ;
+	  AztTimeout = 3000;
+
+	  break;
+
+
+	case AZT_S_READ:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old)  {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_READ\n");
+	  }
+#endif
+	  if (!skip) {
+	      if ((st = aztStatus()) != -1) {
+		if (st & AST_DSK_CHG) {
+		aztDiskChanged = 1;
+		aztTocUpToDate = 0;
+		azt_invalidate_buffers();
+		}
+	      } else break;
+	  } 
+	    
+	  skip = 0;
+	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+	    if (azt_transfer_is_active) {
+	      azt_state = AZT_S_START;
+	      loop_ctl = 1;
+	      break;
+	    }
+	    azt_state = AZT_S_IDLE;
+	    while (CURRENT_VALID)
+	    end_request(0);
+	    return;
+	  }
+
+	  if (CURRENT_VALID) {
+	    struct azt_Play_msf msf;
+	    azt_next_bn = CURRENT -> sector / 4;
+	    azt_hsg2msf(azt_next_bn, &msf.start);
+	    azt_read_count=AZT_BUF_SIZ;    /*??? fast, because we read ahead*/
+/*          azt_read_count= CURRENT->nr_sectors;      slow
+*/
+	    msf.end.min = 0;
+	    msf.end.sec = 0;            
+	    msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
+#ifdef AZT_TEST3
+	    printk("---reading msf-address %x:%x:%x  %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame);
+	    printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
+		    azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+#endif 
+	    sendAztCmd(ACMD_DATA_READ, &msf);
+	    azt_state = AZT_S_DATA;
+	    AztTimeout = READ_TIMEOUT;
+	  } else {
+	    azt_state = AZT_S_STOP;
+	    loop_ctl = 1;
+	    break;
+	  }
+
+	  break;
+
+
+	case AZT_S_DATA:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old)  {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_DATA\n");
+	  }
+#endif
+
+	  st = inb(STATUS_PORT) & AFL_STATUSorDATA;   /*???*/
+
+	  switch (st) {
+
+	    case AFL_DATA:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st; 
+		printk("---AFL_DATA st:%x\n",st);
+	      }
+#endif
+#ifdef WARN_IF_READ_FAILURE
+	      if (AztTries == 5)
+		printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn);
+#endif
+	      if (!AztTries--) {
+		printk("aztcd: read of block %d failed, maybe audio disk ? Giving up\n", azt_next_bn);
+		if (azt_transfer_is_active) {
+		  AztTries = 0;
+		  break;
+		}
+		if (CURRENT_VALID)
+		  end_request(0);
+		AztTries = 5;
+	      }
+	      azt_state = AZT_S_START;
+	      AztTimeout = READ_TIMEOUT;
+	      loop_ctl = 1;
+	      break;
+
+	    case AFL_STATUSorDATA:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st;
+		printk("---AFL_STATUSorDATA st:%x\n",st);
+	      }
+#endif
+	      break;
+
+	    default:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st;
+		printk("---default: st:%x\n",st);
+	      }
+#endif
+	      AztTries = 5;
+	      if (!CURRENT_VALID && azt_buf_in == azt_buf_out) {
+		azt_state = AZT_S_STOP;
+		loop_ctl = 1;
+		break;
+	      }
+	      if (azt_read_count<=0)
+		printk("aztcd: warning - try to read 0 frames\n");
+	      while (azt_read_count)      /*??? fast read ahead loop*/
+	       { azt_buf_bn[azt_buf_in] = -1;
+		 DTEN_LOW;                      /*??? unsolved problem, very
+						      seldom we get timeouts
+						      here, don't now the real
+						      reason. With my drive this
+						      sometimes also happens with
+						      Aztech's original driver under
+						      DOS. Is it a hardware bug? 
+						      I tried to recover from such
+						      situations here. Zimmermann*/
+		 if (aztTimeOutCount>=AZT_TIMEOUT) 
+		  { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in);
+		    printk("azt_transfer_is_active:%x\n",azt_transfer_is_active);
+		    azt_read_count=0;
+		    azt_state = AZT_S_STOP;
+		    loop_ctl = 1;
+		    end_request(1);  /*should we have here (1) or (0)? */
+		  }
+		 else
+		  { insb(DATA_PORT, azt_buf + 2048 * azt_buf_in, 2048);
+		    azt_read_count--;
+#ifdef AZT_TEST3
+		    printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count);
+		    printk("azt_next_bn:%d  azt_buf_in:%d azt_buf_out:%d  azt_buf_bn:%d\n", \
+			 azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+#endif
+		    azt_buf_bn[azt_buf_in] = azt_next_bn++;
+		    if (azt_buf_out == -1)
+		      azt_buf_out = azt_buf_in;
+		    azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1;
+		  }
+	       }
+	      if (!azt_transfer_is_active) {
+		while (CURRENT_VALID) {
+		  azt_transfer();
+		  if (CURRENT -> nr_sectors == 0)
+		    end_request(1);
+		  else
+		    break;
+		}
+	      }
+
+	      if (CURRENT_VALID
+		&& (CURRENT -> sector / 4 < azt_next_bn ||
+		CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) {
+		azt_state = AZT_S_STOP;
+		loop_ctl = 1;
+		break;
+	      }
+	      AztTimeout = READ_TIMEOUT;   
+	      if (azt_read_count==0) {
+		azt_state = AZT_S_STOP;   /*???*/
+		loop_ctl = 1;
+		break;           
+	      } 
+	      break;
+	    }
+    break;
+
+
+	case AZT_S_STOP:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_STOP\n");
+	  }
+#endif
+	  if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count);  /*???*/
+	  while (azt_read_count!=0) {
+	    int i;
+	    if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
+	      for (i=0; i<2048; i++) {
+		inb(DATA_PORT);
+	      }
+	    }
+	    azt_read_count--;
+	  }  
+	  if (aztSendCmd(ACMD_GET_STATUS)) return;
+	  azt_state = AZT_S_STOPPING;
+	  AztTimeout = 1000;
+	  break;
+
+	case AZT_S_STOPPING:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_STOPPING\n");
+	  }
+#endif
+
+	  if ((st = aztStatus()) == -1 && AztTimeout)
+	    break;
+
+	  if ((st != -1) && (st & AST_DSK_CHG)) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+	    azt_invalidate_buffers();
+	  }
+
+
+#ifdef AZT_TEST3
+	  printk("CURRENT_VALID %d azt_mode %d\n",
+	     CURRENT_VALID, azt_mode);
+#endif
+
+	  if (CURRENT_VALID) {
+	    if (st != -1) {
+	      if (azt_mode == 1) {
+		azt_state = AZT_S_READ;
+		loop_ctl = 1;
+		skip = 1;
+		break;
+	      } else {
+		azt_state = AZT_S_MODE;
+		loop_ctl = 1;
+		skip = 1;
+		break;
+	      }
+	    } else {
+	      azt_state = AZT_S_START;
+	      AztTimeout = 1;
+	    }
+	  } else {
+	    azt_state = AZT_S_IDLE;
+	    return;
+	  }
+	  break;
+
+	default:
+	  printk("aztcd: invalid state %d\n", azt_state);
+	  return;
+      }  /* case */
+    } /* while */
+  
+
+   if (!AztTimeout--) 
+    { printk("aztcd: timeout in state %d\n", azt_state);
+      azt_state = AZT_S_STOP;
+      if (aztSendCmd(ACMD_STOP)) return; 
+      STEN_LOW_WAIT;    
+    };
+
+  SET_TIMER(azt_poll, 1);
+}
+
+static void azt_invalidate_buffers(void)
+{ int i;
+
+#ifdef AZT_DEBUG
+  printk("aztcd: executing azt_invalidate_buffers\n");
+#endif
+  for (i = 0; i < AZT_BUF_SIZ; ++i)
+    azt_buf_bn[i] = -1;
+  azt_buf_out = -1;
+}
+
+/*
+ * Open the device special file.  Check that a disk is in.
+ */
+int aztcd_open(struct inode *ip, struct file *fp)
+{       int st;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztcd_open\n");
+#endif
+	if (aztPresent == 0)
+		return -ENXIO;                  /* no hardware */
+
+	if (!azt_open_count && azt_state == AZT_S_IDLE) {
+
+	azt_invalidate_buffers();
+
+	st = getAztStatus();                       /* check drive status */
+	if (st == -1)
+		return -EIO;                    /* drive doesn't respond */
+
+	if ((st&AST_DOOR_OPEN)||(st&AST_NOT_READY)) /* no disk in drive or door open*/
+	{       /*???*/
+		printk("aztcd: no disk in drive or door open\n");
+		return -EIO;
+	}
+
+	if (aztUpdateToc() < 0)
+		return -EIO;
+
+	}
+	++azt_open_count;
+#ifdef AZT_DEBUG
+	printk("aztcd: exiting aztcd_open\n");
+#endif
+	return 0;
+}
+
+
+/*
+ * On close, we flush all azt blocks from the buffer cache.
+ */
+static void aztcd_release(struct inode * inode, struct file * file)
+{ 
+#ifdef AZT_DEBUG
+  printk("aztcd: executing aztcd_release\n");
+  printk("inode: %p, inode->i_rdev: %x    file: %p\n",inode,inode->i_rdev,file);
+#endif
+  if (!--azt_open_count) {
+	azt_invalidate_buffers();
+	sync_dev(inode->i_rdev);             /*??? isn't it a read only dev?*/
+	invalidate_buffers(inode -> i_rdev);
+  }
+  return;
+}
+
+
+static struct file_operations azt_fops = {
+	NULL,                   /* lseek - default */
+	block_read,             /* read - general block-dev read */
+	block_write,            /* write - general block-dev write */
+	NULL,                   /* readdir - bad */
+	NULL,                   /* select */
+	aztcd_ioctl,            /* ioctl */
+	NULL,                   /* mmap */
+	aztcd_open,             /* open */
+	aztcd_release,          /* release */
+	NULL,                   /* fsync */
+	NULL,                   /* fasync*/
+	check_aztcd_media_change, /*media change*/
+	NULL                    /* revalidate*/
+};
+
+/*
+ * Test for presence of drive and initialize it.  Called at boot time.
+ */
+unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end)
+{       long int count, max_count;
+	unsigned char result[50];
+	int st;
+
+	if (azt_port <= 0) {
+	  printk("aztcd: no Aztech CD-ROM Initialization");
+	  return (mem_start);
+	}
+	printk("Aztech CD-ROM Init: Aztech, Orchid, Okano, Wearnes CD-ROM Driver\n");
+	printk("Aztech CD-ROM Init: (C) 1994,1995 Werner Zimmermann\n");
+	printk("Aztech CD-ROM Init: DriverVersion=%s  BaseAddress=0x%x \n",AZT_VERSION,azt_port);
+
+	if (check_region(azt_port, 4)) {
+	  printk("aztcd: conflict, I/O port (%X) already used\n",
+		 azt_port);
+	  return (mem_start);
+	}
+
+	/* check for card */
+	outb(POLLED,MODE_PORT);                 /*???*/
+	inb(CMD_PORT);
+	inb(CMD_PORT);
+	outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/
+	STEN_LOW;
+	if (inb(DATA_PORT)!=AFL_OP_OK)   /*OP_OK? If not, reset and try again*/
+	 { printk("aztcd: drive reset - please wait\n");
+	   for (count=0;count<50;count++)
+	     { inb(STATUS_PORT);    /*removing all data from earlier tries*/
+	       inb(DATA_PORT);
+	     }
+	   outb(POLLED,MODE_PORT);              /*???*/
+	   inb(CMD_PORT);
+	   inb(CMD_PORT);
+	   outb(ACMD_SOFT_RESET,CMD_PORT);   /*send reset*/
+	   STEN_LOW;
+	   if (inb(DATA_PORT)!=AFL_OP_OK)    /*OP_OK?*/
+	    { printk("aztcd: no AZTECH CD-ROM drive found\n");
+	      return (mem_start);
+	    } 
+	   for (count = 0; count < AZT_TIMEOUT; count++);  /* delay a bit */
+	   if ((st=getAztStatus())==-1)
+	    { printk("aztcd: Drive Status Error Status=%x\n",st);
+	      return (mem_start);
+	    }
+#ifdef AZT_DEBUG
+	   printk("aztcd: Status = %x\n",st);
+#endif
+	   outb(POLLED,MODE_PORT);              /*???*/
+	   inb(CMD_PORT);
+	   inb(CMD_PORT);
+	   outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/
+	   STEN_LOW;
+	   OP_OK;
+	 } 
+	azt_init_end=1;
+	STEN_LOW;
+	result[0]=inb(DATA_PORT);        /*reading in a null byte???*/
+	for (count=1;count<50;count++)   /*Reading version string*/
+	 { aztTimeOutCount=0;            /*here we must implement STEN_LOW differently*/
+	   do { aztIndatum=inb(STATUS_PORT);/*because we want to exit by timeout*/
+		aztTimeOutCount++; 
+		if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; 
+	      } while (aztIndatum&AFL_STATUS); 
+	   if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break;  /*all chars read?*/
+	   result[count]=inb(DATA_PORT);
+	 }
+	if (count>30) max_count=30;  /*print max.30 chars of the version string*/
+	else          max_count=count;
+	printk("Aztech CD-ROM Init: FirmwareVersion=");
+	for (count=1;count<max_count;count++) printk("%c",result[count]);
+	printk("<<<\n");
+
+	if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T'))
+	 { printk("Aztech CD-ROM Init: AZTECH drive detected\n"); /*AZTECH*/    
+	 }
+	else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D'))
+	 { printk("Aztech CD-ROM Init: ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
+	 }
+	else                                               /*OTHERS or none*/
+	 { printk("Aztech CD-ROM Init: : unknown drive or firmware version detected\n");
+	   printk("                      azt may not run stable, if you want to try anyhow,\n");
+	   printk("                      boot with: aztcd=base_address,0x79\n");
+	   if ((azt_cont!=0x79))     
+	     { printk("Aztech CD-ROM Init: FirmwareVersion=");
+	       for (count=1;count<5;count++) printk("%c",result[count]);
+	       printk("\n");
+	       printk("Aztech CD-ROM Init: Aborted\n");
+	       return (mem_start);   
+	     }
+	 }
+	if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0)
+	{
+		printk("aztcd: Unable to get major %d for Aztech CD-ROM\n",
+		       MAJOR_NR);
+		return (mem_start);
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 4;
+
+	request_region(azt_port, 4, "aztcd");
+
+	azt_invalidate_buffers();
+	aztPresent = 1;
+	printk("Aztech CD-ROM Init: End\n");
+	return (mem_start);
+}
+
+
+static void azt_hsg2msf(long hsg, struct msf *msf)
+{       hsg += 150;
+	msf -> min = hsg / 4500;
+	hsg %= 4500;
+	msf -> sec = hsg / 75;
+	msf -> frame = hsg % 75;
+#ifdef AZT_DEBUG
+	if (msf->min  >=70) printk("aztcd: Error hsg2msf address Minutes\n");
+	if (msf->sec  >=60) printk("aztcd: Error hsg2msf address Seconds\n");
+	if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n");
+#endif
+	azt_bin2bcd(&msf -> min);           /* convert to BCD */
+	azt_bin2bcd(&msf -> sec);
+	azt_bin2bcd(&msf -> frame);
+}
+
+
+static void azt_bin2bcd(unsigned char *p)
+{       int u, t;
+
+	u = *p % 10;
+	t = *p / 10;
+	*p = u | (t << 4);
+}
+
+static int azt_bcd2bin(unsigned char bcd)
+{       return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+
+
+
+/*
+ * Read a value from the drive.  Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling. The read command itself must
+ * be issued with aztSendCmd() directly before
+ */
+static int aztGetValue(unsigned char *result)
+{       int s;
+
+	STEN_LOW;
+	if (aztTimeOutCount>=AZT_TIMEOUT)
+	{       printk("aztcd: aztGetValue timeout\n");
+		return -1;
+	}
+	s = inb(DATA_PORT) & 0xFF;
+	*result = (unsigned char) s;
+	return 0;
+}
+
+
+/*
+ * Read the current Q-channel info.  Also used for reading the
+ * table of contents.
+ */
+int aztGetQChannelInfo(struct azt_Toc *qp)
+{       unsigned char notUsed;
+	int st;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztGetQChannelInfo\n");
+#endif
+	if ((st=getAztStatus())==-1) return -1;
+	if (aztSendCmd(ACMD_GET_Q_CHANNEL))          return -1;
+	STEN_LOW_WAIT;
+	if (aztGetValue(&notUsed) <0)                return -1; /*Nullbyte ein-*/
+							     /*lesen ???*/
+	if ((st&AST_MODE_BITS)==AST_INITIAL)
+	 { qp->ctrl_addr=0;      /* when audio stop ACMD_GET_Q_CHANNEL returns */
+	   qp->track=0;          /* only one byte with Aztech drives */
+	   qp->pointIndex=0;
+	   qp->trackTime.min=0;
+	   qp->trackTime.sec=0;
+	   qp->trackTime.frame=0;
+	   qp->diskTime.min=0;
+	   qp->diskTime.sec=0;
+	   qp->diskTime.frame=0;
+	   return 0;  
+	 }
+	else
+	 { if (aztGetValue(&qp -> ctrl_addr) < 0)       return -1;
+	   if (aztGetValue(&qp -> track) < 0)           return -1;
+	   if (aztGetValue(&qp -> pointIndex) < 0)      return -1;
+	   if (aztGetValue(&qp -> trackTime.min) < 0)   return -1;
+	   if (aztGetValue(&qp -> trackTime.sec) < 0)   return -1;
+	   if (aztGetValue(&qp -> trackTime.frame) < 0) return -1;
+	   if (aztGetValue(&notUsed) < 0)               return -1;
+	   if (aztGetValue(&qp -> diskTime.min) < 0)    return -1;
+	   if (aztGetValue(&qp -> diskTime.sec) < 0)    return -1;
+	   if (aztGetValue(&qp -> diskTime.frame) < 0)  return -1;
+	 }
+#ifdef AZT_DEBUG
+	printk("aztcd: exiting aztGetQChannelInfo\n");
+#endif
+	return 0;
+}
+
+/*
+ * Read the table of contents (TOC) and TOC header if neccessary
+ */
+static int aztUpdateToc()
+{
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztUpdateToc\n");
+#endif  
+	if (aztTocUpToDate)
+		return 0;
+
+	if (aztGetDiskInfo() < 0)
+		return -EIO;
+
+	if (aztGetToc() < 0)
+		return -EIO;
+
+	aztTocUpToDate = 1;
+#ifdef AZT_DEBUG
+	printk("aztcd: exiting aztUpdateToc\n");
+#endif
+	return 0;
+}
+
+
+/*
+ * Read the table of contents header
+ */
+static int aztGetDiskInfo()
+{ int limit;
+  unsigned char test;
+  struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+  printk("aztcd: starting aztGetDiskInfo\n");
+#endif
+  if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1;
+  STEN_LOW_WAIT;
+  test=0;
+  for (limit=300;limit>0;limit--)
+   {  if (aztGetQChannelInfo(&qInfo)<0) return -1;
+      if (qInfo.pointIndex==0xA0)   /*Number of FirstTrack*/
+	{ DiskInfo.first=qInfo.diskTime.min;
+	  DiskInfo.first = azt_bcd2bin(DiskInfo.first);
+	  test=test|0x01;
+	}
+      if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
+	{ DiskInfo.last=qInfo.diskTime.min;
+	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
+	  test=test|0x02;
+	}
+      if (qInfo.pointIndex==0xA2)   /*DiskLength*/
+	{ DiskInfo.diskLength.min=qInfo.diskTime.min;
+	  DiskInfo.diskLength.sec=qInfo.diskTime.sec-2;
+	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
+	  test=test|0x04;
+	}
+      if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01))   /*StartTime of First Track*/
+	{ DiskInfo.firstTrack.min=qInfo.diskTime.min;
+	  DiskInfo.firstTrack.sec=qInfo.diskTime.sec;
+	  DiskInfo.firstTrack.frame=qInfo.diskTime.frame;
+	  test=test|0x08;
+	}
+      if (test==0x0F) break;
+   }
+#ifdef AZT_DEBUG
+printk ("aztcd: exiting aztGetDiskInfo\n");
+printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
+	DiskInfo.first,
+	DiskInfo.last,
+	DiskInfo.diskLength.min,
+	DiskInfo.diskLength.sec,
+	DiskInfo.diskLength.frame,
+	DiskInfo.firstTrack.min,
+	DiskInfo.firstTrack.sec,
+	DiskInfo.firstTrack.frame);
+#endif
+  if (test!=0x0F) return -1;
+  return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC)
+ */
+static int aztGetToc()
+{       int i, px;
+	int limit;
+	struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztGetToc\n");
+#endif
+	for (i = 0; i < MAX_TRACKS; i++)
+		Toc[i].pointIndex = 0;
+
+	i = DiskInfo.last + 3;
+
+	if (aztSendCmd(ACMD_STOP)) return -1;
+	STEN_LOW_WAIT;
+
+	azt_mode = 0x05;
+	if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1; /*???*/
+	STEN_LOW_WAIT;
+
+	for (limit = 300; limit > 0; limit--)
+	{
+		if (aztGetQChannelInfo(&qInfo) < 0)
+			break;
+
+		px = azt_bcd2bin(qInfo.pointIndex);
+		if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+			if (Toc[px].pointIndex == 0)
+			{
+				Toc[px] = qInfo;
+				i--;
+			}
+
+		if (i <= 0)
+			break;
+	}
+
+	Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+
+#ifdef AZT_DEBUG
+printk("aztcd: exiting aztGetToc\n");
+for (i = 1; i <= DiskInfo.last+1; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+for (i = 100; i < 103; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+#endif
+
+	return limit > 0 ? 0 : -1;
+}
+
diff -u --recursive --new-file v1.1.82/linux/drivers/block/blk.h linux/drivers/block/blk.h
--- v1.1.82/linux/drivers/block/blk.h	Mon Jan  9 07:22:00 1995
+++ linux/drivers/block/blk.h	Wed Jan 18 21:00:26 1995
@@ -44,6 +44,9 @@
 
 extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
 extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
+#ifdef CONFIG_AZTCD
+extern unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end);
+#endif
 #ifdef CONFIG_BLK_DEV_HD
 extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
 #endif
@@ -166,6 +169,14 @@
 #define DEVICE_NAME "Mitsumi CD-ROM"
 /* #define DEVICE_INTR do_mcd */
 #define DEVICE_REQUEST do_mcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
+
+#define DEVICE_NAME "Aztech CD-ROM"
+#define DEVICE_REQUEST do_aztcd_request
 #define DEVICE_NR(device) (MINOR(device))
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
diff -u --recursive --new-file v1.1.82/linux/drivers/block/cdu31a.c linux/drivers/block/cdu31a.c
--- v1.1.82/linux/drivers/block/cdu31a.c	Mon Jan 16 14:18:15 1995
+++ linux/drivers/block/cdu31a.c	Wed Jan 18 09:38:59 1995
@@ -78,16 +78,9 @@
  *
  * Multi-Session
  *
- * A multi-session disk is treated like a partitioned disk, each session
- * has it's own minor device number, starting with 0.  The support is
- * pretty transparent, music, TOC operations, and read operations should
- * all work transparently on any session.  Note that since the driver
- * writer doesn't have a multi-session disk, this is all theoretical.
- * Also, music operation will obviously only work on one session at a
- * time.
- *
- * NOTE: At the current time, multi-session still doesn't work.  Maybe
- * I'll get a multi-session disk soon so I can play with it.
+ * A multi-session disk looks just like a normal disk to the user.
+ * Just mount one normally, and all the data should be there.
+ * A special thanks to Koen for help with this!
  * 
  * Raw sector I/O
  *
@@ -226,7 +219,7 @@
 
 static int handle_sony_cd_attention(void);
 static int read_subcode(void);
-static void sony_get_toc(int dev);
+static void sony_get_toc(void);
 static int scd_open(struct inode *inode, struct file *filp);
 static void do_sony_cd_cmd(unsigned char cmd,
                            unsigned char *params,
@@ -274,9 +267,12 @@
 static int sony_pas_init = 0;		   /* Initialize the Pro-Audio
 					      Spectrum card? */
 
-static struct s_sony_session_toc *(ses_tocs[MAX_TRACKS]); /* Points to the
-							     table of
-							     contents. */
+static struct s_sony_session_toc *sony_toc; /* Points to the
+					       table of
+					       contents. */
+
+static int sony_toc_read = 0;		   /* Has the TOC been read for
+					      the drive? */
 
 static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
                                                     subcode address read */
@@ -317,11 +313,10 @@
 
 static int curr_control_reg = 0; /* Current value of the control register */
 
-/* A disk changed variable for every possible session.  When a disk change
-   is detected, these will all be set to TRUE.  As the upper layers ask
-   for disk_changed status for individual minor numbers, they will be
-   cleared. */
-static char disk_changed[MAX_TRACKS];
+/* A disk changed variable.  When a disk change is detected, it will
+   all be set to TRUE.  As the upper layers ask for disk_changed status
+   it will be cleared. */
+static char disk_changed;
 
 /* Variable for using the readahead buffer.  The readahead buffer
    is used for raw sector reads and for blocksizes that are smaller
@@ -338,18 +333,10 @@
 static int
 scd_disk_change(dev_t full_dev)
 {
-   int retval, target;
-
-
-   target = MINOR(full_dev);
-
-   if (target >= MAX_TRACKS) {
-      printk("Sony CD-ROM request error: invalid device.\n");
-      return 0;
-   }
+   int retval;
 
-   retval = disk_changed[target];
-   disk_changed[target] = 0;
+   retval = disk_changed;
+   disk_changed = 0;
 
    return retval;
 }
@@ -859,7 +846,6 @@
 {
    unsigned char atten_code;
    static int num_consecutive_attentions = 0;
-   int i;
 
 
    if (is_attention())
@@ -879,15 +865,8 @@
       {
        /* Someone changed the CD.  Mark it as changed */
       case SONY_MECH_LOADED_ATTN:
-         for (i=0; i<MAX_TRACKS; i++)
-         {
-            disk_changed[i] = 1;
-	    if (ses_tocs[i] != NULL)
-	    {
-	       kfree_s(ses_tocs[i], sizeof(struct s_sony_session_toc));
-	       ses_tocs[i] = NULL;
-	    }
-         }
+         disk_changed = 1;
+	 sony_toc_read = 0;
          sony_audio_status = CDROM_AUDIO_NO_STATUS;
          sony_blocks_left = 0;
          break;
@@ -1000,8 +979,7 @@
 static int
 start_request(unsigned int sector,
               unsigned int nsect,
-              int          read_nsect_only,
-	      int          dev)
+              int          read_nsect_only)
 {
    unsigned char params[6];
    unsigned int read_size;
@@ -1018,9 +996,9 @@
     * If the full read-ahead would go beyond the end of the media, trim
     * it back to read just till the end of the media.
     */
-   else if ((sector + nsect) >= ses_tocs[dev]->lead_out_start_lba)
+   else if ((sector + nsect) >= sony_toc->lead_out_start_lba)
    {
-      read_size = ses_tocs[dev]->lead_out_start_lba - sector;
+      read_size = sony_toc->lead_out_start_lba - sector;
    }
    /* Read the full readahead amount. */
    else
@@ -1374,7 +1352,6 @@
 do_cdu31a_request(void)
 {
    int block;
-   unsigned int dev;
    int nblock;
    unsigned char res_reg[12];
    unsigned int res_size;
@@ -1440,36 +1417,24 @@
          }
       }
 
-      dev = MINOR(CURRENT->dev);
       block = CURRENT->sector;
       nblock = CURRENT->nr_sectors;
 
-      /* Check for multi-session disks. */
-      if (dev > MAX_TRACKS)
-      {
-	 printk("CDU31A: Invalid device request: %d\n", dev);
-	 end_request(0);
-	 goto cdu31a_request_startover;
-      }
-      else if (ses_tocs[dev] == NULL)
+      if (!sony_toc_read)
       {
-	 printk("CDU31A: session TOC not read: %d\n", dev);
+	 printk("CDU31A: TOC not read\n");
 	 end_request(0);
 	 goto cdu31a_request_startover;
       }
 
-      /* Check for base read of multi-session disk. */
-      if ((dev != 0) && (block == 64))
+      /* Check for base read of multi-session disk.  This will still work
+         for single session disks, so just do it.  Blocks less than 80
+         are for the volume info, so offset them by the start track (which
+         should be zero for a single-session disk). */
+      if (block < 80)
       {
-         if (ses_tocs[dev]->first_track_num == ses_tocs[dev]->last_track_num)
-         {
-            printk("CDU31A: Not a multi-session disk: %d\n", dev);
-            end_request(0);
-            goto cdu31a_request_startover;
-         }
-
 	 /* Offset the request into the session. */
-	 block += (ses_tocs[dev]->start_track_lba * 4);
+	 block += (sony_toc->start_track_lba * 4);
       }
 
       switch(CURRENT->cmd)
@@ -1479,19 +1444,21 @@
           * If the block address is invalid or the request goes beyond the end of
           * the media, return an error.
           */
-	 if ((block / 4) < ses_tocs[dev]->start_track_lba)
+#if 0
+	 if ((block / 4) < sony_toc->start_track_lba)
 	 {
             printk("CDU31A: Request before beginning of media\n");
             end_request(0);
             goto cdu31a_request_startover;
 	 }
-         if ((block / 4) >= ses_tocs[dev]->lead_out_start_lba)
+#endif
+         if ((block / 4) >= sony_toc->lead_out_start_lba)
          {
             printk("CDU31A: Request past end of media\n");
             end_request(0);
             goto cdu31a_request_startover;
          }
-         if (((block + nblock) / 4) >= ses_tocs[dev]->lead_out_start_lba)
+         if (((block + nblock) / 4) >= sony_toc->lead_out_start_lba)
          {
             printk("CDU31A: Request past end of media\n");
             end_request(0);
@@ -1504,9 +1471,9 @@
          while (handle_sony_cd_attention())
             ;
 
-         if (ses_tocs[dev] == NULL)
+         if (!sony_toc_read)
          {
-	    printk("CDU31A: session TOC not read: %d\n", dev);
+	    printk("CDU31A: TOC not read\n");
 	    end_request(0);
 	    goto cdu31a_request_startover;
          }
@@ -1515,7 +1482,7 @@
 	    next request. */
          if (sony_blocks_left == 0)
          {
-            if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev))
+            if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
             {
                end_request(0);
                goto cdu31a_request_startover;
@@ -1532,13 +1499,13 @@
                    sony_next_block);
 #endif
             abort_read();
-            if (ses_tocs[dev] == NULL)
+            if (!sony_toc_read)
             {
-	       printk("CDU31A: session TOC not read: %d\n", dev);
+	       printk("CDU31A: TOC not read\n");
 	       end_request(0);
                goto cdu31a_request_startover;
             }
-            else if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev))
+            if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
             {
                end_request(0);
                goto cdu31a_request_startover;
@@ -1581,7 +1548,7 @@
    }
 
 end_do_cdu31a_request:
-#if 0
+#if 1
    /* After finished, cancel any pending operations. */
    abort_read();
 #endif
@@ -1614,93 +1581,137 @@
  * successful.
  */
 static void
-sony_get_toc(int dev)
+sony_get_toc(void)
 {
+   unsigned char res_reg[2];
    unsigned int res_size;
    unsigned char parms[1];
+   int session;
 
 
-   if (dev >= MAX_TRACKS)
-   {
-      printk("CDU31A: Request for invalid TOC track: %d\n", dev);
-   }
-   else if (ses_tocs[dev] == NULL)
+#if DEBUG
+   printk("Entering sony_get_toc\n");
+#endif
+
+   if (!sony_toc_read)
    {
-      ses_tocs[dev] = kmalloc(sizeof(struct s_sony_session_toc), 0);
-      if (ses_tocs[dev] == NULL)
-      {
-	 printk("CDU31A: Unable to alloc mem for TOC\n");
-      }
-      else
+      /* The idea here is we keep asking for sessions until the command
+	 fails.  Then we know what the last valid session on the disk is.
+	 No need to check session 0, since session 0 is the same as session
+         1; the command returns different information if you give it 0. 
+         Don't check session 1 because that is the first session, it must
+         be there. */
+      session = 2;
+      while (1)
       {
-	 parms[0] = dev+1;
-	 do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
+#if DEBUG
+         printk("Trying session %d\n", session);
+#endif
+	 parms[0] = session;
+	 do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD,
 			parms,
 			1, 
-			(unsigned char *) ses_tocs[dev], 
+			res_reg,
 			&res_size);
-	 if ((res_size < 2) || ((ses_tocs[dev]->exec_status[0] & 0xf0) == 0x20))
-	 {
-	    kfree_s(ses_tocs[dev], sizeof(struct s_sony_session_toc));
-	    ses_tocs[dev] = NULL;
-	    return;
-	 }
 
-         /* For points that do not exist, move the data over them
-            to the right location. */
-         if (ses_tocs[dev]->pointb0 != 0xb0)
-         {
-            mcovlp(((char *) ses_tocs[dev]) + 27,
-                   ((char *) ses_tocs[dev]) + 18,
-                   res_size - 18);
-            res_size += 9;
-         }
-         if (ses_tocs[dev]->pointb1 != 0xb1)
-         {
-            mcovlp(((char *) ses_tocs[dev]) + 36,
-                   ((char *) ses_tocs[dev]) + 27,
-                   res_size - 27);
-            res_size += 9;
-         }
-         if (ses_tocs[dev]->pointb2 != 0xb2)
-         {
-            mcovlp(((char *) ses_tocs[dev]) + 45,
-                   ((char *) ses_tocs[dev]) + 36,
-                   res_size - 36);
-            res_size += 9;
-         }
-         if (ses_tocs[dev]->pointb3 != 0xb3)
-         {
-            mcovlp(((char *) ses_tocs[dev]) + 54,
-                   ((char *) ses_tocs[dev]) + 45,
-                   res_size - 45);
-            res_size += 9;
-         }
-         if (ses_tocs[dev]->pointb4 != 0xb4)
-         {
-            mcovlp(((char *) ses_tocs[dev]) + 63,
-                   ((char *) ses_tocs[dev]) + 54,
-                   res_size - 54);
-            res_size += 9;
+#if DEBUG
+         printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]);
+#endif
+
+	 if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+	 {
+	    /* An error reading the TOC, this must be past the last session. */
+	    break;
          }
-         if (ses_tocs[dev]->pointc0 != 0xc0)
+
+         session++;
+
+         /* Let's not get carried away... */
+         if (session > 20)
          {
-            mcovlp(((char *) ses_tocs[dev]) + 72,
-                   ((char *) ses_tocs[dev]) + 63,
-                   res_size - 63);
-            res_size += 9;
+            return;
          }
+      }
+
+      session--;
 
-	 ses_tocs[dev]->start_track_lba = msf_to_log(ses_tocs[dev]->tracks[0].track_start_msf);
-	 ses_tocs[dev]->lead_out_start_lba = msf_to_log(ses_tocs[dev]->lead_out_start_msf);
 #if DEBUG
-	 printk("Disk session %d, start track: %d, stop track: %d\n",
-		dev,
-		ses_tocs[dev]->start_track_lba,
-		ses_tocs[dev]->lead_out_start_lba);
+      printk("Reading session %d\n", session);
 #endif
+
+      parms[0] = session;
+      do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
+		     parms,
+		     1, 
+		     (unsigned char *) sony_toc, 
+		     &res_size);
+      if ((res_size < 2) || ((sony_toc->exec_status[0] & 0xf0) == 0x20))
+      {
+	 /* An error reading the TOC.  Return without sony_toc_read
+	    set. */
+	 return;
+      }
+      
+      sony_toc_read = 1;
+
+      /* For points that do not exist, move the data over them
+	 to the right location. */
+      if (sony_toc->pointb0 != 0xb0)
+      {
+	 mcovlp(((char *) sony_toc) + 27,
+		((char *) sony_toc) + 18,
+		res_size - 18);
+	 res_size += 9;
+      }
+      if (sony_toc->pointb1 != 0xb1)
+      {
+	 mcovlp(((char *) sony_toc) + 36,
+		((char *) sony_toc) + 27,
+		res_size - 27);
+	 res_size += 9;
       }
+      if (sony_toc->pointb2 != 0xb2)
+      {
+	 mcovlp(((char *) sony_toc) + 45,
+		((char *) sony_toc) + 36,
+		res_size - 36);
+	 res_size += 9;
+      }
+      if (sony_toc->pointb3 != 0xb3)
+      {
+	 mcovlp(((char *) sony_toc) + 54,
+		((char *) sony_toc) + 45,
+		res_size - 45);
+	 res_size += 9;
+      }
+      if (sony_toc->pointb4 != 0xb4)
+      {
+	 mcovlp(((char *) sony_toc) + 63,
+		((char *) sony_toc) + 54,
+		res_size - 54);
+	 res_size += 9;
+      }
+      if (sony_toc->pointc0 != 0xc0)
+      {
+	 mcovlp(((char *) sony_toc) + 72,
+		((char *) sony_toc) + 63,
+		res_size - 63);
+	 res_size += 9;
+      }
+
+      sony_toc->start_track_lba = msf_to_log(sony_toc->tracks[0].track_start_msf);
+      sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+
+#if DEBUG
+   printk("Disk session %d, start track: %d, stop track: %d\n",
+	  session,
+	  sony_toc->start_track_lba,
+	  sony_toc->lead_out_start_lba);
+#endif
    }
+#if DEBUG
+   printk("Leaving sony_get_toc\n");
+#endif
 }
 
 
@@ -1708,17 +1719,16 @@
  * Search for a specific track in the table of contents.
  */
 static int
-find_track(int track,
-	   int dev)
+find_track(int track)
 {
    int i;
    int num_tracks;
 
 
-   num_tracks = ses_tocs[dev]->last_track_num - ses_tocs[dev]->first_track_num + 1;
+   num_tracks = sony_toc->last_track_num - sony_toc->first_track_num + 1;
    for (i = 0; i < num_tracks; i++)
    {
-      if (ses_tocs[dev]->tracks[i].track == track)
+      if (sony_toc->tracks[i].track == track)
       {
          return i;
       }
@@ -1761,8 +1771,7 @@
  * (not BCD), so all the conversions are done.
  */
 static int
-sony_get_subchnl_info(long arg,
-		      int  dev)
+sony_get_subchnl_info(long arg)
 {
    struct cdrom_subchnl schi;
 
@@ -1771,8 +1780,8 @@
    while (handle_sony_cd_attention())
       ;
 
-   sony_get_toc(dev);
-   if (ses_tocs[dev] == NULL)
+   sony_get_toc();
+   if (!sony_toc_read)
    {
       return -EIO;
    }
@@ -2025,7 +2034,7 @@
 
    retval = 0;
    /* start_request clears out any readahead data, so it should be safe. */
-   if (start_request(ra->addr.lba, ra->nframes, 1, 0))
+   if (start_request(ra->addr.lba, ra->nframes, 1))
    {
       retval = -EIO;
       goto exit_read_audio;
@@ -2064,7 +2073,7 @@
             }
 
             /* Restart the request on the current frame. */
-            if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1, 0))
+            if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1))
             {
                retval = -EIO;
                goto exit_read_audio;
@@ -2164,7 +2173,6 @@
           unsigned int  cmd,
           unsigned long arg)
 {
-   unsigned int dev;
    unsigned char res_reg[12];
    unsigned int res_size;
    unsigned char params[7];
@@ -2175,11 +2183,6 @@
    {
       return -EINVAL;
    }
-   dev = MINOR(inode->i_rdev);
-   if (dev > MAX_TRACKS)
-   {
-      return -EINVAL;
-   }
 
    switch (cmd)
    {
@@ -2289,16 +2292,16 @@
          struct cdrom_tochdr *hdr;
          struct cdrom_tochdr loc_hdr;
          
-         sony_get_toc(dev);
-         if (ses_tocs[dev] == NULL)
+         sony_get_toc();
+         if (!sony_toc_read)
          {
             return -EIO;
          }
          
          hdr = (struct cdrom_tochdr *) arg;
          verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
-         loc_hdr.cdth_trk0 = bcd_to_int(ses_tocs[dev]->first_track_num);
-         loc_hdr.cdth_trk1 = bcd_to_int(ses_tocs[dev]->last_track_num);
+         loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+         loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
          memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
       }
       return 0;
@@ -2311,8 +2314,8 @@
          int track_idx;
          unsigned char *msf_val = NULL;
          
-         sony_get_toc(dev);
-         if (ses_tocs[dev] == NULL)
+         sony_get_toc();
+         if (!sony_toc_read)
          {
             return -EIO;
          }
@@ -2326,21 +2329,21 @@
          /* Lead out is handled separately since it is special. */
          if (loc_entry.cdte_track == CDROM_LEADOUT)
          {
-            loc_entry.cdte_adr = ses_tocs[dev]->address2;
-            loc_entry.cdte_ctrl = ses_tocs[dev]->control2;
-            msf_val = ses_tocs[dev]->lead_out_start_msf;
+            loc_entry.cdte_adr = sony_toc->address2;
+            loc_entry.cdte_ctrl = sony_toc->control2;
+            msf_val = sony_toc->lead_out_start_msf;
          }
          else
          {
-            track_idx = find_track(int_to_bcd(loc_entry.cdte_track), dev);
+            track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
             if (track_idx < 0)
             {
                return -EINVAL;
             }
             
-            loc_entry.cdte_adr = ses_tocs[dev]->tracks[track_idx].address;
-            loc_entry.cdte_ctrl = ses_tocs[dev]->tracks[track_idx].control;
-            msf_val = ses_tocs[dev]->tracks[track_idx].track_start_msf;
+            loc_entry.cdte_adr = sony_toc->tracks[track_idx].address;
+            loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
+            msf_val = sony_toc->tracks[track_idx].track_start_msf;
          }
          
          /* Logical buffer address or MSF format requested? */
@@ -2364,8 +2367,8 @@
          struct cdrom_ti ti;
          int track_idx;
          
-         sony_get_toc(dev);
-         if (ses_tocs[dev] == NULL)
+         sony_get_toc();
+         if (!sony_toc_read)
          {
             return -EIO;
          }
@@ -2373,39 +2376,39 @@
          verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
          
          memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
-         if (   (ti.cdti_trk0 < ses_tocs[dev]->first_track_num)
-             || (ti.cdti_trk0 > ses_tocs[dev]->last_track_num)
+         if (   (ti.cdti_trk0 < sony_toc->first_track_num)
+             || (ti.cdti_trk0 > sony_toc->last_track_num)
              || (ti.cdti_trk1 < ti.cdti_trk0))
          {
             return -EINVAL;
          }
          
-         track_idx = find_track(int_to_bcd(ti.cdti_trk0), dev);
+         track_idx = find_track(int_to_bcd(ti.cdti_trk0));
          if (track_idx < 0)
          {
             return -EINVAL;
          }
-         params[1] = ses_tocs[dev]->tracks[track_idx].track_start_msf[0];
-         params[2] = ses_tocs[dev]->tracks[track_idx].track_start_msf[1];
-         params[3] = ses_tocs[dev]->tracks[track_idx].track_start_msf[2];
+         params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
+         params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
+         params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
          
          /*
           * If we want to stop after the last track, use the lead-out
           * MSF to do that.
           */
-         if (ti.cdti_trk1 >= bcd_to_int(ses_tocs[dev]->last_track_num))
+         if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
          {
-            log_to_msf(msf_to_log(ses_tocs[dev]->lead_out_start_msf)-1,
+            log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
                        &(params[4]));
          }
          else
          {
-            track_idx = find_track(int_to_bcd(ti.cdti_trk1+1), dev);
+            track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
             if (track_idx < 0)
             {
                return -EINVAL;
             }
-            log_to_msf(msf_to_log(ses_tocs[dev]->tracks[track_idx].track_start_msf)-1,
+            log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
                        &(params[4]));
          }
          params[0] = 0x03;
@@ -2430,7 +2433,7 @@
       }
      
    case CDROMSUBCHNL:   /* Get subchannel info */
-      return sony_get_subchnl_info(arg, dev);
+      return sony_get_subchnl_info(arg);
 
    case CDROMVOLCTRL:   /* Volume control.  What volume does this change, anyway? */
       {
@@ -2471,8 +2474,8 @@
          struct cdrom_read_audio ra;
 
 
-         sony_get_toc(dev);
-         if (ses_tocs[dev] == NULL)
+         sony_get_toc();
+         if (!sony_toc_read)
          {
             return -EIO;
          }
@@ -2484,8 +2487,8 @@
 
          if (ra.addr_format == CDROM_LBA)
          {
-            if (   (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba)
-                || (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba))
+            if (   (ra.addr.lba >= sony_toc->lead_out_start_lba)
+                || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba))
             {
                return -EINVAL;
             }
@@ -2502,8 +2505,8 @@
             ra.addr.lba = (  (ra.addr.msf.minute * 4500)
                            + (ra.addr.msf.second * 75)
                            + ra.addr.msf.frame);
-            if (   (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba)
-                || (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba))
+            if (   (ra.addr.lba >= sony_toc->lead_out_start_lba)
+                || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba))
             {
                return -EINVAL;
             }
@@ -2541,7 +2544,6 @@
    unsigned int res_size;
    int num_spin_ups;
    unsigned char params[2];
-   int dev;
 
 
    if ((filp) && filp->f_mode & 2)
@@ -2589,9 +2591,8 @@
          return -EIO;
       }
 
-      dev = MINOR(inode->i_rdev);
-      sony_get_toc(dev);
-      if (ses_tocs[dev] == NULL)
+      sony_get_toc();
+      if (!sony_toc_read)
       {
          do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
          return -EIO;
@@ -2599,7 +2600,7 @@
 
       /* For XA on the CDU31A only, we have to do special reads.
          The CDU33A handles XA automagically. */
-      if (   (ses_tocs[dev]->disk_type == SONY_XA_DISK_TYPE)
+      if (   (sony_toc->disk_type == SONY_XA_DISK_TYPE)
           && (!is_double_speed))
       {
          params[0] = SONY_SD_DECODE_PARAM;
@@ -2954,13 +2955,12 @@
       mem_start += sizeof(*last_sony_subcode);
       readahead_buffer = (unsigned char *) mem_start;
       mem_start += CD_FRAMESIZE_RAW;
+      sony_toc = (struct s_sony_session_toc *) mem_start;
+      mem_start += sizeof(struct s_sony_session_toc);
    }
 
 
-   for (i=0; i<MAX_TRACKS; i++)
-   {
-      disk_changed[i] = 1;
-   }
+   disk_changed = 1;
    
    return mem_start;
 }
diff -u --recursive --new-file v1.1.82/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.1.82/linux/drivers/block/ide.c	Mon Jan 16 14:18:16 1995
+++ linux/drivers/block/ide.c	Tue Jan 17 15:13:15 1995
@@ -1218,7 +1218,7 @@
 
 #if SUPPORT_SHARING_IRQ
 /* entry point for all interrupts on ide0/ide1 when sharing_single_irq==1 */
-static void ide_shared_intr (int irq)
+static void ide_shared_intr (int irq, struct pt_regs * regs)
 {
 	IDE_INTR(current_hwif);
 }
diff -u --recursive --new-file v1.1.82/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.1.82/linux/drivers/block/ll_rw_blk.c	Sun Jan  1 16:28:20 1995
+++ linux/drivers/block/ll_rw_blk.c	Wed Jan 18 21:00:26 1995
@@ -547,6 +547,9 @@
 #ifdef CONFIG_MCD
 	mem_start = mcd_init(mem_start,mem_end);
 #endif
+#ifdef CONFIG_AZTCD
+        mem_start = aztcd_init(mem_start,mem_end);
+#endif
 #ifdef CONFIG_BLK_DEV_FD
 	floppy_init();
 #else
diff -u --recursive --new-file v1.1.82/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c
--- v1.1.82/linux/drivers/block/sbpcd.c	Mon Jan 16 14:18:18 1995
+++ linux/drivers/block/sbpcd.c	Wed Jan 18 07:51:44 1995
@@ -153,11 +153,11 @@
  *       experiments by Serge Robyns.
  *       First attempts to support the TEAC CD-55A drives; but still not
  *       usable yet.
- *       Implemented the CDROMMULTISESSION and CDROMMULTISESSION_SYS ioctls;
- *       this is an attempt to handle multi session CDs more "transparent"
- *       (redirection handling has to be done within the isofs routines, and
- *       only for the special purpose of obtaining the "right" volume
- *       descriptor; accesses to the raw device should not get redirected).
+ *       Implemented the CDROMMULTISESSION ioctl; this is an attempt to handle
+ *       multi session CDs more "transparent" (redirection handling has to be
+ *       done within the isofs routines, and only for the special purpose of
+ *       obtaining the "right" volume descriptor; accesses to the raw device
+ *       should not get redirected).
  *
  *  3.0  Just a "normal" increment, with some provisions to do it better. ;-)
  *       Introduced "#define READ_AUDIO" to specify the maximum number of 
@@ -3472,7 +3472,7 @@
 	return (0);
       } /* end of CDROMREADAUDIO */
 
-    case CDROMMULTISESSION: /* tell start-of-last-session to user */
+    case CDROMMULTISESSION: /* tell start-of-last-session */
       DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMMULTISESSION entered.\n"));
       st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_multisession));
       if (st) return (st);
@@ -3489,17 +3489,6 @@
       memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
       DPRINTF((DBG_MUL,"SBPCD: ioctl: CDROMMULTISESSION done (%d, %08X).\n",
 	       ms_info.xa_flag, ms_info.addr.lba));
-      return (0);
-
-    case CDROMMULTISESSION_SYS: /* tell start-of-last-session to kernel */
-      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMMULTISESSION_SYS entered.\n"));
-      if(!suser()) return -EACCES;
-      if (DriveStruct[d].f_multisession)
-	*((unsigned int *) arg)=DriveStruct[d].lba_multi;
-      else
-	*((unsigned int *) arg)=0;
-      DPRINTF((DBG_MUL,"SBPCD: ioctl: CDROMMULTISESSION_SYS done (%d).\n",
-	       ((unsigned int *) arg)[0]));
       return (0);
 
     case BLKRASET:
diff -u --recursive --new-file v1.1.82/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.1.82/linux/drivers/char/console.c	Mon Jan 16 14:18:18 1995
+++ linux/drivers/char/console.c	Wed Jan 18 10:18:08 1995
@@ -1082,7 +1082,7 @@
 	if (can_do_color)
 		for (p = (unsigned short *)origin; p < (unsigned short *)scr_end; p++) {
 			unsigned short old = readw(p);
-			writew((old & 0x88ff) | (((old >> 4) | (old << 4)) & 0x7700), p);
+			writew((old & 0x88ff) | (((old >> 4) | ((old & 0xff00) << 4)) & 0x7700), p);
 		}
 	else
 		for (p = (unsigned short *)origin; p < (unsigned short *)scr_end; p++) {
@@ -2004,6 +2004,7 @@
 	scr_end	= video_mem_start + video_num_lines * video_size_row;
 	gotoxy(currcons,orig_x,orig_y);
 	set_origin(currcons);
+	csi_J(currcons, 0);
 	printable = 1;
 	printk("Console: %s %s %ldx%ld, %d virtual console%s (max %d)\n",
 		can_do_color ? "colour" : "mono",
diff -u --recursive --new-file v1.1.82/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.1.82/linux/drivers/char/serial.c	Mon Jan 16 14:18:19 1995
+++ linux/drivers/char/serial.c	Wed Jan 18 22:52:36 1995
@@ -941,6 +941,10 @@
 		info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
 		info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
 	}
+#ifdef __alpha__
+	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
+	info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
+#endif
 	if (info->irq == 0)
 		info->MCR = info->MCR_noint;
 	serial_outp(info, UART_MCR, info->MCR);
@@ -2358,7 +2362,15 @@
 	/*
 	 * Reset the UART.
 	 */
+#ifdef __alpha__
+	/*
+	 * I wonder what DEC did to the OUT1 and OUT2 lines?
+	 * clearing them results in endless interrupts.
+	 */
+	serial_outp(info, UART_MCR, 0x0c);
+#else
 	serial_outp(info, UART_MCR, 0x00);
+#endif
 	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
 				     UART_FCR_CLEAR_XMIT));
 	(void)serial_in(info, UART_RX);
diff -u --recursive --new-file v1.1.82/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.1.82/linux/drivers/char/tty_io.c	Mon Jan  9 07:22:04 1995
+++ linux/drivers/char/tty_io.c	Wed Jan 18 21:26:34 1995
@@ -1443,7 +1443,8 @@
 			arg = get_fs_long((unsigned long *) arg);
 			return tty_set_ldisc(tty, arg);
 		case TIOCLINUX:
-			if (current->tty != tty && !suser())
+			if ((current->tty != tty || 
+			     tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) && !suser())
 				return -EPERM;
 			retval = verify_area(VERIFY_READ, (void *) arg, 1);
 			if (retval)
diff -u --recursive --new-file v1.1.82/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v1.1.82/linux/drivers/char/vt.c	Sun Jan  1 19:49:19 1995
+++ linux/drivers/char/vt.c	Tue Jan 17 00:57:01 1995
@@ -364,7 +364,7 @@
 		const struct kbentry * a = (struct kbentry *)arg;
 		ushort *key_map;
 		u_char s;
-		u_short v;
+		u_short v, ov;
 
 		if (!perm)
 			return -EPERM;
@@ -379,7 +379,7 @@
 		if (!i && v == K_NOSUCHMAP) {
 			/* disallocate map */
 			key_map = key_maps[s];
-			if (key_map) {
+			if (s && key_map) {
 			    key_maps[s] = 0;
 			    if (key_map[0] == U(K_ALLOCATED)) {
 				kfree_s(key_map, sizeof(plain_map));
@@ -401,6 +401,8 @@
 			return 0;
 
 		if (!(key_map = key_maps[s])) {
+			int j;
+
 			if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
 				return -EPERM;
 
@@ -410,18 +412,22 @@
 				return -ENOMEM;
 			key_maps[s] = key_map;
 			key_map[0] = U(K_ALLOCATED);
-			for (s = 1; s < NR_KEYS; s++)
-				key_map[s] = U(K_HOLE);
+			for (j = 1; j < NR_KEYS; j++)
+				key_map[j] = U(K_HOLE);
 			keymap_count++;
 		}
+		ov = U(key_map[i]);
+		if (v == ov)
+			return 0;	/* nothing to do */
 		/*
 		 * Only the Superuser can set or unset the Secure
 		 * Attention Key.
 		 */
-		if (((key_map[i] == U(K_SAK)) || (v == K_SAK)) &&
-		    !suser())
+		if (((ov == K_SAK) || (v == K_SAK)) && !suser())
 			return -EPERM;
 		key_map[i] = U(v);
+		if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+			compute_shiftstate();
 		return 0;
 	}
 
diff -u --recursive --new-file v1.1.82/linux/drivers/net/de600.c linux/drivers/net/de600.c
--- v1.1.82/linux/drivers/net/de600.c	Mon Jan 16 14:18:20 1995
+++ linux/drivers/net/de600.c	Wed Jan 18 10:43:57 1995
@@ -96,6 +96,7 @@
 #include <linux/fcntl.h>
 #include <linux/string.h>
 #include <linux/interrupt.h>
+#include <linux/ioport.h>
 #include <asm/io.h>
 #include <linux/in.h>
 #include <linux/ptrace.h>
@@ -261,7 +262,6 @@
 /*
  * D-Link driver variables:
  */
-extern struct device		*irq2dev_map[16];
 static volatile int		rx_page		= 0;
 
 #define TX_PAGES 2
@@ -686,6 +686,12 @@
 		return ENODEV;
 	}
 
+	if (check_region(DE600_IO, 3)) {
+		printk(", port 0x%x busy\n", DE600_IO);
+		return EBUSY;
+	}
+	request_region(DE600_IO, 3, "de600");
+
 	printk(", Ethernet Address: %02X", dev->dev_addr[0]);
 	for (i = 1; i < ETH_ALEN; i++)
 		printk(":%02X",dev->dev_addr[i]);
@@ -838,5 +844,6 @@
 		printk("de600: device busy, remove delayed\n");
 	else
 		unregister_netdev(&de600_dev);
+	release_region(DE600_IO, 3);
 }
 #endif /* MODULE */
diff -u --recursive --new-file v1.1.82/linux/drivers/net/de620.c linux/drivers/net/de620.c
--- v1.1.82/linux/drivers/net/de620.c	Mon Jan 16 14:18:20 1995
+++ linux/drivers/net/de620.c	Wed Jan 18 10:43:57 1995
@@ -100,6 +100,13 @@
 #define COUNT_LOOPS
  */
 #endif
+static int bnc, utp;
+/*
+ * Force media with insmod:
+ *	insmod de620.o bnc=1
+ * or
+ *	insmod de620.o utp=1
+ */
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -107,6 +114,7 @@
 #include <linux/fcntl.h>
 #include <linux/string.h>
 #include <linux/interrupt.h>
+#include <linux/ioport.h>
 #include <asm/io.h>
 #include <linux/in.h>
 #include <linux/ptrace.h>
@@ -192,7 +200,6 @@
 #define DE620_RX_START_PAGE 12		/* 12 pages (=3k) reserved for tx */
 #define DEF_NIC_CMD IRQEN | ICEN | DS1
 
-extern struct device *irq2dev_map[16];
 unsigned int de620_debug = DE620_DEBUG;
 
 static volatile byte	NIC_Cmd;
@@ -719,6 +726,11 @@
 		EIPRegister = NCTL0 | NIS0;
 	}
 
+	if (utp)
+		EIPRegister = NCTL0 | NIS0;
+	if (bnc)
+		EIPRegister = NCTL0;
+
 	de620_send_command(W_CR | RNOP | CLEAR);
 	de620_send_command(W_CR | RNOP);
 
@@ -812,6 +824,12 @@
 		return ENODEV;
 	}
 
+	if (check_region(DE620_IO, 3)) {
+		printk(", port 0x%x busy\n", DE620_IO);
+		return EBUSY;
+	}
+	request_region(DE620_IO, 3, "de620");
+
 	/* else, got it! */
 	printk(", Ethernet Address: %2.2X",
 		dev->dev_addr[0] = nic_data.NodeID[0]);
@@ -973,6 +991,7 @@
 		printk("de620: device busy, remove delayed\n");
 	else
 		unregister_netdev(&de620_dev);
+	release_region(DE620_IO, 3);
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/ChangeLog linux/drivers/scsi/ChangeLog
--- v1.1.82/linux/drivers/scsi/ChangeLog	Mon Jan 16 14:18:21 1995
+++ linux/drivers/scsi/ChangeLog	Wed Jan 18 09:38:36 1995
@@ -1,3 +1,97 @@
+Mon Jan 16 07:18:23 1995  Eric Youngdale  (eric@andante)
+
+	* Linux 1.1.82 released.
+
+	Throughout.
+	- Change all interrupt handlers to accept new calling convention.
+	In particular, we now receive the irq number as one of the arguments.
+
+	* More minor spelling corrections in some of the new files.
+
+	* aha1542.c, buslogic.c: Clean up interrupt handler a little now
+	that we receive the irq as an arg.
+
+	* aha274x.c: s/snarf_region/request_region/
+
+	* eata.c: Update to version 1.12.   Fix some comments and display a
+	message if we cannot reserve the port addresses.
+
+	* u14-34f.c: Update to version 1.13.   Fix some comments and display a
+	message if we cannot reserve the port addresses.
+
+	* eata_dma.c: Define get_board_data function (send INQUIRY command).
+	Use to improve detection of variants of different DPT boards.  Change
+	version subnumber to "0g".
+
+	* fdomain.c:  Update to version 5.26.  Improve detection of some boards
+	repackaged by IBM.
+
+	* scsi.c (scsi_register_host): Change "name" to const char *.
+
+	* sr.c: Fix problem in set mode command for Toshiba drives.
+
+	* sr.c: Fix typo from patch 81.
+
+Fri Jan 13 12:54:46 1995  Eric Youngdale  (eric@andante)
+
+	* Linux 1.1.81 released.  Codefreeze for 1.2 release announced.
+
+	Big changes here.
+
+	* eata_dma.*: New files from Michael Neuffer.
+	(neuffer@goofy.zdv.uni-mainz.de).  Should support
+	all eata/dpt cards.
+
+	* hosts.c, Makefile: Add eata_dma.
+
+	* README.st: Document MTEOM.
+
+	Patches from me (ERY) to finish support for low-level loadable scsi.
+	It now works, and is actually useful.
+
+	* Throughout - add new argument to scsi_init_malloc that takes an
+	additional parameter.  This is used as a priority to kmalloc,
+	and you can specify the GFP_DMA flag if you need DMA-able memory.
+
+	* Makefile: For source files that are loadable, always add name
+	to SCSI_SRCS.  Fill in modules: target.
+
+	* hosts.c:  Change next_host to next_scsi_host, and make global.
+	Print hosts after we have identified all of them.  Use info()
+	function if present, otherwise use name field.
+
+	* hosts.h: Change attach function to return int, not void.
+	Define number of device slots to allow for loadable devices.
+	Define tags to tell scsi module code what type of module we
+	are loading.
+
+	* scsi.c: Fix scan_scsis so that it can be run by a user process.
+	Do not use waiting loops - use up and down mechanism as long
+	as current != task[0].
+
+	* scsi.c(scan_scsis): Do not use stack variables for I/O - this
+	could be > 16Mb if we are loading a module at runtime (i.e. use
+	scsi_init_malloc to get some memory we know will be safe).
+
+	* scsi.c: Change dma freelist to be a set of pages.  This allows
+	us to dynamicly adjust the size of the list by adding more pages
+	to the pagelist.  Fix scsi_malloc and scsi_free accordingly.
+
+	* scsi_module.c: Fix include.
+
+	* sd.c: Declare detach function.  Increment/decrement module usage
+	count as required.  Fix init functions to allow loaded devices.
+	Revalidate all new disks so we get the partition tables.  Define
+	detach function.
+
+	* sr.c: Likewise.
+
+	* sg.c: Declare detach function.  Allow attachment of devices on
+	loaded drivers.
+
+	* st.c: Declare detach function.  Increment/decrement module usage
+	count as required.
+
 Tue Jan 10 10:09:58 1995  Eric Youngdale  (eric@andante)
 
 	* Linux 1.1.79 released.
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v1.1.82/linux/drivers/scsi/aha1542.c	Mon Jan 16 14:18:22 1995
+++ linux/drivers/scsi/aha1542.c	Wed Jan 18 09:38:36 1995
@@ -2,7 +2,7 @@
  *  linux/kernel/aha1542.c
  *
  *  Copyright (C) 1992  Tommy Thorn
- *  Copyright (C) 1993, 1994 Eric Youngdale
+ *  Copyright (C) 1993, 1994, 1995 Eric Youngdale
  *
  *  Modified by Eric Youngdale
  *        Use request_irq and request_dma to help prevent unexpected conflicts
@@ -1093,7 +1093,6 @@
 int aha1542_abort(Scsi_Cmnd * SCpnt)
 {
 #if 0
-  int intval[3];
   unchar ahacmd = CMD_START_SCSI;
   unsigned long flags;
   struct mailbox * mb;
@@ -1119,8 +1118,7 @@
   if(mb[mbi].status) {
     printk("Lost interrupt discovered on irq %d - attempting to recover\n", 
 	   SCpnt->host->irq);
-    intval[0] = SCpnt->host->irq;
-    aha1542_intr_handle((int) &intval[2]);
+    aha1542_intr_handle(SCpnt->host->irq, NULL);
     return 0;
   }
 
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/buslogic.c linux/drivers/scsi/buslogic.c
--- v1.1.82/linux/drivers/scsi/buslogic.c	Mon Jan 16 14:18:22 1995
+++ linux/drivers/scsi/buslogic.c	Tue Jan 17 00:42:53 1995
@@ -1408,10 +1408,7 @@
 			" - attempting to recover...\n",
 			scpnt->host->irq);
 	{
-	    int intval[3];
-
-	    intval[0] = scpnt->host->irq;
-	    buslogic_interrupt((int)&intval[2]);
+	    buslogic_interrupt(scpnt->host->irq, NULL);
 	    return SCSI_ABORT_SUCCESS;
 	}
     }
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v1.1.82/linux/drivers/scsi/eata_dma.c	Mon Jan 16 14:18:22 1995
+++ linux/drivers/scsi/eata_dma.c	Mon Jan 16 23:35:54 1995
@@ -695,8 +695,8 @@
 
     buff = get_board_data((uint)base, gc->IRQ, gc->scsi_id[3]);
 
-    if(!(strncmp("PM2322", &buff[16], 6) || strncmp("PM3021", &buff[16], 6)
-       || strncmp("PM3222", &buff[16], 6) || strncmp("PM3224", &buff[16], 6)))
+    if(strncmp("PM2322", &buff[16], 6) && strncmp("PM3021", &buff[16], 6)
+       && strncmp("PM3222", &buff[16], 6) && strncmp("PM3224", &buff[16], 6))
       gc->MAX_CHAN = 0;
     
     if (gc->MAX_CHAN) {
@@ -732,7 +732,9 @@
 	sh->can_queue = ntohs(gc->queuesiz) / (gc->MAX_CHAN + 1);
 
 	if (gc->OCS_enabled == TRUE) {
-	    sh->cmd_per_lun = sh->can_queue/C_P_L_DIV; 
+	    sh->cmd_per_lun = sh->can_queue/C_P_L_DIV;
+            if (sh->cmd_per_lun < 2)
+                sh->cmd_per_lun = 2;
 	} else {
 	    sh->cmd_per_lun = 1;
 	}
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/eata_dma.h linux/drivers/scsi/eata_dma.h
--- v1.1.82/linux/drivers/scsi/eata_dma.h	Mon Jan 16 14:18:22 1995
+++ linux/drivers/scsi/eata_dma.h	Tue Jan 17 00:57:01 1995
@@ -377,7 +377,7 @@
   unchar p_code:6,
            null:1,
          p_save:1;
-  unchar p_lenght;
+  unchar p_length;
   ushort cylinder;
   unchar heads;
   unchar sectors;
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.1.82/linux/drivers/scsi/hosts.c	Fri Jan 13 16:57:05 1995
+++ linux/drivers/scsi/hosts.c	Wed Jan 18 09:38:36 1995
@@ -1,7 +1,10 @@
 /*
  *	hosts.c Copyright (C) 1992 Drew Eckhardt
- *	mid to lowlevel SCSI driver interface by
- *		Drew Eckhardt
+ *	        Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ *	mid to lowlevel SCSI driver interface
+ *		Initial versions: Drew Eckhardt
+ *		Subsequent revisions: Eric Youngdale
  *
  *	<drew@colorado.edu>
  */
@@ -329,7 +332,8 @@
 		      shpnt->host_no, name);
 	    }
 
-	printk ("scsi : %d hosts.\n", next_scsi_host);
+	printk ("scsi : %d host%s.\n", next_scsi_host,
+		(next_scsi_host == 1) ? "" : "s");
 
       {
       int block_count = 0, index;
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.1.82/linux/drivers/scsi/scsi.c	Mon Jan 16 14:18:23 1995
+++ linux/drivers/scsi/scsi.c	Wed Jan 18 09:38:36 1995
@@ -1,9 +1,10 @@
 /*
  *	scsi.c Copyright (C) 1992 Drew Eckhardt 
- *	       Copyright (C) 1993, 1994 Eric Youngdale
+ *	       Copyright (C) 1993, 1994, 1995 Eric Youngdale
  *
- *	generic mid-level SCSI driver by
- *		Drew Eckhardt 
+ *	generic mid-level SCSI driver
+ *		Initial versions: Drew Eckhardt 
+ *		Subsequent revisions: Eric Youngdale
  *
  *	<drew@colorado.edu>
  *
@@ -1132,10 +1133,7 @@
 		case NO_SENSE:
 			return 0;
 		case RECOVERED_ERROR:
-			if (SCpnt->device->type == TYPE_TAPE)
-			  return SUGGEST_IS_OK;
-			else
-			  return 0;
+			return SUGGEST_IS_OK;
 
 		case ABORTED_COMMAND:
 			return SUGGEST_RETRY;	
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.1.82/linux/drivers/scsi/sd.c	Fri Jan 13 16:57:06 1995
+++ linux/drivers/scsi/sd.c	Wed Jan 18 09:38:36 1995
@@ -1,14 +1,19 @@
 /*
  *	sd.c Copyright (C) 1992 Drew Eckhardt 
- *	     Copyright (C) 1993, 1994 Eric Youngdale
- *	Linux scsi disk driver by
- *		Drew Eckhardt 
+ *	     Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ *	Linux scsi disk driver
+ *		Initial versions: Drew Eckhardt 
+ *		Subsequent revisions: Eric Youngdale
  *
  *	<drew@colorado.edu>
  *
  *       Modified by Eric Youngdale ericy@cais.com to
  *       add scatter-gather, multiple outstanding request, and other
  *       enhancements.
+ *
+ *	 Modified by Eric Youngdale eric@aib.com to support loadable
+ *	 low-level scsi drivers.
  */
 
 #include <linux/fs.h>
@@ -80,6 +85,12 @@
 	if(target >= sd_template.dev_max || !rscsi_disks[target].device)
 	  return -ENXIO;   /* No such device */
 	
+	/*
+	 * See if we are requesting a non-existant partition.
+	 */
+	if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+	  return -ENXIO;
+
 /* Make sure that only one process can do a check_change_disk at one time.
  This is also used to lock out further access when the partition table is being re-read. */
 
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v1.1.82/linux/drivers/scsi/seagate.c	Mon Jan 16 14:18:23 1995
+++ linux/drivers/scsi/seagate.c	Wed Jan 18 09:38:36 1995
@@ -96,7 +96,9 @@
 						used to calculate memory mapped
 						register location.
 					*/
+#ifdef notyet
 static volatile int abort_confirm = 0;
+#endif
 
 static volatile void *st0x_cr_sr;       /*
 						control register write,
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.1.82/linux/drivers/scsi/sr.c	Mon Jan 16 14:18:23 1995
+++ linux/drivers/scsi/sr.c	Wed Jan 18 09:38:36 1995
@@ -1,6 +1,6 @@
 /*
  *      sr.c Copyright (C) 1992 David Giller
- *	     Copyright (C) 1993, 1994 Eric Youngdale
+ *	     Copyright (C) 1993, 1994, 1995 Eric Youngdale
  *
  *      adapted from:
  *	sd.c Copyright (C) 1992 Drew Eckhardt 
@@ -12,6 +12,9 @@
  *       Modified by Eric Youngdale ericy@cais.com to
  *       add scatter-gather, multiple outstanding request, and other
  *       enhancements.
+ *
+ *	 Modified by Eric Youngdale eric@aib.com to support loadable
+ *	 low-level scsi drivers.
  */
 
 #include <linux/fs.h>
@@ -31,7 +34,7 @@
 #include "constants.h"
 
 #define MAX_RETRIES 3
-#define SR_TIMEOUT 5000
+#define SR_TIMEOUT 15000
 
 static void sr_init(void);
 static void sr_finish(void);
@@ -236,14 +239,16 @@
 		}
 	    
 		if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
-			printk("CD-ROM error: Drive reports ILLEGAL REQUEST.\n");
+			printk("CD-ROM error: ");
+			print_sense("sr", SCpnt);
+			printk("command was: ");
+			print_command(SCpnt->cmnd);
 			if (scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten) {
 				scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten = 0;
 				requeue_sr_request(SCpnt);
 				result = 0;
 				return;
 			} else {
-			  printk("CD-ROM error: Drive reports %d.\n", SCpnt->sense_buffer[2]);				
 			  SCpnt = end_scsi_request(SCpnt, 0, this_count);
 			  requeue_sr_request(SCpnt); /* Do next request */
 			  return;
@@ -958,8 +963,14 @@
 		  /* If we have already seen this, then skip it.  Comes up
 		     with loadable modules. */
 		  if (scsi_CDs[i].capacity) continue;
+		  scsi_CDs[i].capacity = 0x1fffff;
+		  scsi_CDs[i].sector_size = 2048;  /* A guess, just in case */
+		  scsi_CDs[i].needs_sector_size = 1;
+#if 0
+		  /* seems better to leave this for later */
 		  get_sectorsize(i);
 		  printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
+#endif
 		  scsi_CDs[i].use = 1;
 		  scsi_CDs[i].ten = 1;
 		  scsi_CDs[i].remap = 1;
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v1.1.82/linux/drivers/scsi/sr_ioctl.c	Wed Jan 11 21:14:27 1995
+++ linux/drivers/scsi/sr_ioctl.c	Wed Jan 18 07:51:45 1995
@@ -420,11 +420,6 @@
 			  return (0);
 			}
 
-		case CDROMMULTISESSION_SYS: /* tell start-of-last-session to kernel */
-			if(!suser()) return -EACCES;
-			*((unsigned int *)arg)=scsi_CDs[target].mpcd_sector;
-			return (0);
-			
 		case BLKRASET:
 			if(!suser())  return -EACCES;
 			if(!inode->i_rdev) return -EINVAL;
diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c
--- v1.1.82/linux/drivers/scsi/ultrastor.c	Mon Jan 16 14:18:23 1995
+++ linux/drivers/scsi/ultrastor.c	Mon Jan 16 16:48:19 1995
@@ -873,7 +873,7 @@
 	printk("Ux4F: abort while completed command pending\n");
 	restore_flags(flags);
 	cli();
-	ultrastor_interrupt(0);
+	ultrastor_interrupt(0, NULL);
 	restore_flags(flags);
 	return SCSI_ABORT_SUCCESS;  /* FIXME - is this correct? -ERY */
       }
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v1.1.82/linux/drivers/sound/ad1848.c	Tue Oct 11 08:51:31 1994
+++ linux/drivers/sound/ad1848.c	Tue Jan 17 07:49:57 1995
@@ -93,7 +93,7 @@
 static int      ad1848_prepare_for_IO (int dev, int bsize, int bcount);
 static void     ad1848_reset (int dev);
 static void     ad1848_halt (int dev);
-void            ad1848_interrupt (int dev);
+void            ad1848_interrupt (int dev, struct pt_regs * regs);
 
 static int
 ad_read (ad1848_info * devc, int reg)
@@ -866,7 +866,7 @@
 }
 
 void
-ad1848_interrupt (int irq)
+ad1848_interrupt (int irq, struct pt_regs * regs)
 {
   unsigned char   status;
   ad1848_info    *devc;
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v1.1.82/linux/drivers/sound/configure.c	Sun Nov  6 13:43:22 1994
+++ linux/drivers/sound/configure.c	Wed Jan 18 09:38:59 1995
@@ -135,7 +135,7 @@
 
   "SoundBlaster Pro support",
   "SoundBlaster 16 support",
-  "digitized voice support",
+  "/dev/dsp and /dev/audio support (_recommended_)",
   "This should not be asked",
   "MIDI interface support",
   "This should not be asked",
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v1.1.82/linux/drivers/sound/gus_card.c	Mon Jul 18 09:50:55 1994
+++ linux/drivers/sound/gus_card.c	Tue Jan 17 07:49:57 1995
@@ -33,7 +33,7 @@
 
 #include "gus_hw.h"
 
-void            gusintr (int);
+void            gusintr (int, struct pt_regs * regs);
 
 int             gus_base, gus_irq, gus_dma;
 extern int      gus_wave_volume;
@@ -118,7 +118,7 @@
 }
 
 void
-gusintr (int irq)
+gusintr (int irq, struct pt_regs * regs)
 {
   unsigned char   src;
 
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v1.1.82/linux/drivers/sound/gus_wave.c	Mon Dec 12 21:19:00 1994
+++ linux/drivers/sound/gus_wave.c	Tue Jan 17 07:49:57 1995
@@ -781,7 +781,7 @@
 
   gus_select_voice (0);		/* This disables writes to IRQ/DMA reg */
 
-  gusintr (0);			/* Serve pending interrupts */
+  gusintr (0,NULL);		/* Serve pending interrupts */
   RESTORE_INTR (flags);
 }
 
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v1.1.82/linux/drivers/sound/mpu401.c	Mon Jul 18 09:50:55 1994
+++ linux/drivers/sound/mpu401.c	Tue Jan 17 07:49:57 1995
@@ -479,7 +479,7 @@
 }
 
 void
-mpuintr (int irq)
+mpuintr (int irq, struct pt_regs * regs)
 {
   struct mpu_config *devc;
   int             dev;
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v1.1.82/linux/drivers/sound/pas2_card.c	Mon Jul 18 09:50:55 1994
+++ linux/drivers/sound/pas2_card.c	Tue Jan 17 07:49:57 1995
@@ -79,7 +79,7 @@
 /******************* Begin of the Interrupt Handler ********************/
 
 void
-pasintr (int unused)
+pasintr (int unused, struct pt_regs * regs)
 {
   int             status;
 
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c
--- v1.1.82/linux/drivers/sound/sb_dsp.c	Fri Aug 19 08:54:09 1994
+++ linux/drivers/sound/sb_dsp.c	Wed Jan 18 09:38:50 1995
@@ -131,7 +131,7 @@
 }
 
 void
-sbintr (int unit)
+sbintr (int unit, struct pt_regs *regs)
 {
   int             status;
 
@@ -803,7 +803,7 @@
 
   if (sbc_major >= 3)
     {
-#ifndef SCO
+#if !defined(SCO) && !defined(EXCLUDE_AUDIO)
 #  ifdef __SGNXPRO__
       if (mixer_type == 2)
 	{
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v1.1.82/linux/drivers/sound/sound_calls.h	Wed Jul 20 15:00:55 1994
+++ linux/drivers/sound/sound_calls.h	Tue Jan 17 07:49:57 1995
@@ -100,7 +100,7 @@
 void request_sound_timer (int count);
 void sound_stop_timer(void);
 int snd_ioctl_return(int *addr, int value);
-int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int));
+int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int, struct pt_regs *));
 void snd_release_irq(int vect);
 void sound_dma_malloc(int dev);
 void sound_dma_free(int dev);
@@ -177,7 +177,7 @@
 long attach_gus_card(long mem_start, struct address_info * hw_config);
 int probe_gus(struct address_info *hw_config);
 int gus_set_midi_irq(int num);
-void gusintr(int);
+void gusintr(int, struct pt_regs * regs);
 long attach_gus_db16(long mem_start, struct address_info * hw_config);
 int probe_gus_db16(struct address_info *hw_config);
 
diff -u --recursive --new-file v1.1.82/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c
--- v1.1.82/linux/drivers/sound/uart6850.c	Mon Jul 18 09:50:55 1994
+++ linux/drivers/sound/uart6850.c	Tue Jan 17 07:49:57 1995
@@ -93,7 +93,7 @@
 }
 
 void
-m6850intr (int unit)
+m6850intr (int unit, struct pt_regs * regs)
 {
   printk ("M");
   if (input_avail ())
diff -u --recursive --new-file v1.1.82/linux/fs/buffer.c linux/fs/buffer.c
--- v1.1.82/linux/fs/buffer.c	Mon Jan  9 07:22:08 1995
+++ linux/fs/buffer.c	Wed Jan 18 09:31:39 1995
@@ -741,20 +741,19 @@
 }
 
 
-static char buffer_disposition[] = {BUF_CLEAN, BUF_SHARED, BUF_LOCKED, BUF_SHARED, 
-				      BUF_DIRTY, BUF_DIRTY, BUF_DIRTY, BUF_DIRTY};
-
 void refile_buffer(struct buffer_head * buf){
-	int i, dispose;
-	i = 0;
+	int dispose;
 	if(buf->b_dev == 0xffff) panic("Attempt to refile free buffer\n");
-	if(mem_map[MAP_NR((unsigned long) buf->b_data)] != 1) i = 1;
-	if(buf->b_lock) i |= 2;
-	if(buf->b_dirt) i |= 4;
-	dispose = buffer_disposition[i];
-	if(buf->b_list == BUF_SHARED && dispose == BUF_CLEAN)
-		 dispose = BUF_UNSHARED;
-	if(dispose == -1) panic("Bad buffer settings (%d)\n", i);
+	if (buf->b_dirt)
+		dispose = BUF_DIRTY;
+	else if (mem_map[MAP_NR((unsigned long) buf->b_data)] > 1)
+		dispose = BUF_SHARED;
+	else if (buf->b_lock)
+		dispose = BUF_LOCKED;
+	else if (buf->b_list == BUF_SHARED)
+		dispose = BUF_UNSHARED;
+	else
+		dispose = BUF_CLEAN;
 	if(dispose == BUF_CLEAN) buf->b_lru_time = jiffies;
 	if(dispose != buf->b_list)  {
 		if(dispose == BUF_DIRTY || dispose == BUF_UNSHARED)
@@ -1211,6 +1210,9 @@
 	return 1;
 }
 
+
+/* =========== Reduce the buffer memory ============= */
+
 /*
  * try_to_free() checks if all the buffers on this particular page
  * are unused, and free's the page if so.
@@ -1386,6 +1388,8 @@
 }
 
 
+/* ================== Debugging =================== */
+
 void show_buffers(void)
 {
 	struct buffer_head * bh;
@@ -1425,6 +1429,9 @@
 	}
 }
 
+
+/* ====================== Cluster patches for ext2 ==================== */
+
 /*
  * try_to_reassign() checks if all the buffers on this particular page
  * are unused, and reassign to a new cluster them if this is true.
@@ -1556,7 +1563,7 @@
 	bh->b_this_page = tmp;
 	while (nblock-- > 0)
 		brelse(arr[nblock]);
-	return 4;
+	return 4; /* ?? */
 not_aligned:
 	while ((tmp = bh) != NULL) {
 		bh = bh->b_this_page;
@@ -1592,6 +1599,9 @@
 		 return reassign_cluster(dev, b[0], size);
 }
 
+
+/* ===================== Init ======================= */
+
 /*
  * This initializes the initial buffer free list.  nr_buffers_type is set
  * to one less the actual number of buffers, as a sop to backwards
@@ -1630,6 +1640,9 @@
 		panic("VFS: Unable to initialize buffer free list!");
 	return;
 }
+
+
+/* ====================== bdflush support =================== */
 
 /* This is a simple kernel daemon, whose job it is to provide a dynamically
  * response to dirty buffers.  Once this process is activated, we write back
diff -u --recursive --new-file v1.1.82/linux/fs/exec.c linux/fs/exec.c
--- v1.1.82/linux/fs/exec.c	Wed Jan 11 21:14:27 1995
+++ linux/fs/exec.c	Wed Jan 18 09:31:39 1995
@@ -505,7 +505,6 @@
 	int i;
 	int ch;
 	char * name;
-	struct vm_area_struct * mpnt, *mpnt1;
 
 	current->dumpable = 1;
 	name = bprm->filename;
@@ -517,20 +516,9 @@
 				current->comm[i++] = ch;
 	}
 	current->comm[i] = '\0';
-	/* Release all of the old mmap stuff. */
 
-	mpnt = current->mm->mmap;
-	current->mm->mmap = NULL;
-	while (mpnt) {
-		mpnt1 = mpnt->vm_next;
-		if (mpnt->vm_ops && mpnt->vm_ops->close)
-			mpnt->vm_ops->close(mpnt);
-		remove_shared_vm_struct(mpnt);
-		if (mpnt->vm_inode)
-			iput(mpnt->vm_inode);
-		kfree(mpnt);
-		mpnt = mpnt1;
-	}
+	/* Release all of the old mmap stuff. */
+	exit_mmap(current);
 
 	flush_thread();
 
diff -u --recursive --new-file v1.1.82/linux/fs/ext/namei.c linux/fs/ext/namei.c
--- v1.1.82/linux/fs/ext/namei.c	Wed Jan 11 21:14:27 1995
+++ linux/fs/ext/namei.c	Wed Jan 18 09:51:48 1995
@@ -285,6 +285,7 @@
 				de->rec_len = rec_len;
 			}
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+			dir->i_dirt = 1;
 			de->name_len = namelen;
 			for (i=0; i < namelen ; i++)
 				de->name[i] = name[i];
diff -u --recursive --new-file v1.1.82/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v1.1.82/linux/fs/isofs/inode.c	Wed Jan 11 21:14:28 1995
+++ linux/fs/isofs/inode.c	Wed Jan 18 07:51:45 1995
@@ -154,6 +154,7 @@
 	int high_sierra;
 	int dev=s->s_dev;
 	int i;
+	struct cdrom_multisession ms_info;
 	unsigned int vol_desc_start;
 	struct inode inode_fake;
 	extern struct file_operations * get_blkfops(unsigned int);
@@ -204,16 +205,26 @@
 	 * <emoenke@gwdg.de>
 	 */
 	vol_desc_start=0;
-	inode_fake.i_rdev=dev;
-	i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
-					 NULL,
-					 CDROMMULTISESSION_SYS,
-					 (unsigned long) &vol_desc_start);
+	if (get_blkfops(MAJOR(dev))->ioctl!=NULL)
+	  {
+	    inode_fake.i_rdev=dev;
+	    ms_info.addr_format=CDROM_LBA;
+	    set_fs(KERNEL_DS);
+	    i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
+					     NULL,
+					     CDROMMULTISESSION,
+					     (unsigned long) &ms_info);
+	    set_fs(USER_DS);
 #if 0
-	printk("isofs.inode: CDROMMULTISESSION_SYS rc=%d\n",i);
-	printk("isofs.inode: vol_desc_start = %d\n", vol_desc_start);
+	    printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
+	    if (i==0)
+	      {
+		printk("isofs.inode: XA disk: %s\n", ms_info.xa_flag ? "yes":"no");
+		printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba);
+	      }
 #endif 0
-	if (i!=0) vol_desc_start=0;
+	    if ((i==0)&&(ms_info.xa_flag)) vol_desc_start=ms_info.addr.lba;
+	  }
 	for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) {
 #if 0
 	printk("isofs.inode: iso_blknum=%d\n", iso_blknum);
diff -u --recursive --new-file v1.1.82/linux/fs/namei.c linux/fs/namei.c
--- v1.1.82/linux/fs/namei.c	Wed Jan 11 21:14:28 1995
+++ linux/fs/namei.c	Wed Jan 18 09:31:40 1995
@@ -16,6 +16,7 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/stat.h>
+#include <linux/mm.h>
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
 
@@ -31,13 +32,8 @@
 
 	if (get_fs() == KERNEL_DS)
 		return 0;
-	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			return -EFAULT;
-		if (vma->vm_end > address)
-			break;
-	}
-	if (vma->vm_start > address || !(vma->vm_page_prot & PAGE_USER))
+	vma = find_vma(current, address);
+	if (!vma || vma->vm_start > address || !(vma->vm_page_prot & PAGE_USER))
 		return -EFAULT;
 	address = vma->vm_end - address;
 	if (address > PAGE_SIZE)
diff -u --recursive --new-file v1.1.82/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.1.82/linux/fs/proc/array.c	Fri Jan 13 10:12:23 1995
+++ linux/fs/proc/array.c	Wed Jan 18 09:51:48 1995
@@ -22,6 +22,9 @@
  *
  * Jeff Tranter      :  added BogoMips field to cpuinfo
  *                      <Jeff_Tranter@Mitel.COM>
+ *
+ * Bruno Haible      :  remove 4K limit for the maps file
+ * <haible@ma2s2.mathematik.uni-karlsruhe.de>
  */
 
 #include <linux/types.h>
@@ -49,6 +52,7 @@
 int get_malloc(char * buffer);
 #endif
 
+
 static int read_core(struct inode * inode, struct file * file,char * buf, int count)
 {
 	unsigned long p = file->f_pos;
@@ -103,6 +107,7 @@
 	&proc_kcore_operations, 
 };
 
+
 #ifdef CONFIG_PROFILE
 
 extern unsigned long prof_len;
@@ -161,6 +166,7 @@
 
 #endif /* CONFIG_PROFILE */
 
+
 static int get_loadavg(char * buffer)
 {
 	int a, b, c;
@@ -549,21 +555,64 @@
 		       size, resident, share, trs, lrs, drs, dt);
 }
 
-static int get_maps(int pid, char *buf)
+/*
+ * The way we support synthetic files > 4K
+ * - without storing their contents in some buffer and
+ * - without walking through the entire synthetic file until we reach the
+ *   position of the requested data
+ * is to cleverly encode the current position in the file's f_pos field.
+ * There is no requirement that a read() call which returns `count' bytes
+ * of data increases f_pos by exactly `count'.
+ *
+ * This idea is Linus' one. Bruno implemented it.
+ */
+
+/*
+ * For the /proc/<pid>/maps file, we use fixed length records, each containing
+ * a single line.
+ */
+#define MAPS_LINE_LENGTH	1024
+#define MAPS_LINE_SHIFT		10
+/*
+ * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH
+ *         + (index into the line)
+ */
+#define MAPS_LINE_FORMAT	  "%08lx-%08lx %s %08lx %02x:%02x %lu\n"
+#define MAPS_LINE_MAX	49 /* sum of 8  1  8  1 4 1 8  1  2 1  2 1 10 1 */
+
+static int read_maps (int pid, struct file * file, char * buf, int count)
 {
-	int sz = 0;
-	struct task_struct **p = get_task(pid);
-	struct vm_area_struct *map;
+	struct task_struct ** p = get_task(pid);
+	char * destptr;
+	loff_t lineno;
+	int column;
+	struct vm_area_struct * map;
+	int i;
 
 	if (!p || !*p)
+		return -EINVAL;
+
+	if (count == 0)
 		return 0;
 
-	for(map = (*p)->mm->mmap; map != NULL; map = map->vm_next) {
-		char str[7], *cp = str;
+	/* decode f_pos */
+	lineno = file->f_pos >> MAPS_LINE_SHIFT;
+	column = file->f_pos & (MAPS_LINE_LENGTH-1);
+
+	/* quickly go to line lineno */
+	for (map = (*p)->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++)
+		continue;
+
+	destptr = buf;
+
+	for ( ; map ; ) {
+		/* produce the next line */
+		char line[MAPS_LINE_MAX+1];
+		char str[5], *cp = str;
 		int flags;
-		int end = sz + 80;	/* Length of line */
 		dev_t dev;
 		unsigned long ino;
+		int len;
 
 		flags = map->vm_flags;
 
@@ -572,12 +621,7 @@
 		*cp++ = flags & VM_EXEC ? 'x' : '-';
 		*cp++ = flags & VM_SHARED ? 's' : 'p';
 		*cp++ = 0;
-		
-		if (end >= PAGE_SIZE) {
-			sprintf(buf+sz, "...\n");
-			break;
-		}
-		
+
 		if (map->vm_inode != NULL) {
 			dev = map->vm_inode->i_dev;
 			ino = map->vm_inode->i_ino;
@@ -586,16 +630,44 @@
 			ino = 0;
 		}
 
-		sz += sprintf(buf+sz, "%08lx-%08lx %s %08lx %02x:%02x %lu\n",
+		len = sprintf(line, MAPS_LINE_FORMAT,
 			      map->vm_start, map->vm_end, str, map->vm_offset,
 			      MAJOR(dev),MINOR(dev), ino);
-		if (sz > end) {
-			printk("get_maps: end(%d) < sz(%d)\n", end, sz);
-			break;
+
+		if (column >= len) {
+			column = 0; /* continue with next line at column 0 */
+			lineno++;
+			map = map->vm_next;
+			continue;
+		}
+
+		i = len-column;
+		if (i > count)
+			i = count;
+		memcpy_tofs(destptr, line+column, i);
+		destptr += i; count -= i;
+		column += i;
+		if (column >= len) {
+			column = 0; /* next time: next line at column 0 */
+			lineno++;
+			map = map->vm_next;
 		}
+
+		/* done? */
+		if (count == 0)
+			break;
+
+		/* By writing to user space, we might have slept.
+		 * Stop the loop, to avoid a race condition.
+		 */
+		if (*p != current)
+			break;
 	}
-	
-	return sz;
+
+	/* encode f_pos */
+	file->f_pos = (lineno << MAPS_LINE_SHIFT) + column;
+
+	return destptr-buf;
 }
 
 extern int get_module_list(char *);
@@ -673,8 +745,6 @@
 			return get_stat(pid, page);
 		case PROC_PID_STATM:
 			return get_statm(pid, page);
-		case PROC_PID_MAPS:
-			return get_maps(pid, page);
 	}
 	return -EBADF;
 }
@@ -734,6 +804,52 @@
 
 struct inode_operations proc_array_inode_operations = {
 	&proc_array_operations,	/* default base directory file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+static int arraylong_read (struct inode * inode, struct file * file, char * buf, int count)
+{
+	unsigned int pid = inode->i_ino >> 16;
+	unsigned int type = inode->i_ino & 0x0000ffff;
+
+	if (count < 0)
+		return -EINVAL;
+
+	switch (type) {
+		case PROC_PID_MAPS:
+			return read_maps(pid, file, buf, count);
+	}
+	return -EINVAL;
+}
+
+static struct file_operations proc_arraylong_operations = {
+	NULL,		/* array_lseek */
+	arraylong_read,
+	NULL,		/* array_write */
+	NULL,		/* array_readdir */
+	NULL,		/* array_select */
+	NULL,		/* array_ioctl */
+	NULL,		/* mmap */
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* can't fsync */
+};
+
+struct inode_operations proc_arraylong_inode_operations = {
+	&proc_arraylong_operations,	/* default base directory file-ops */
 	NULL,			/* create */
 	NULL,			/* lookup */
 	NULL,			/* link */
diff -u --recursive --new-file v1.1.82/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v1.1.82/linux/fs/proc/inode.c	Wed Jan 11 21:14:28 1995
+++ linux/fs/proc/inode.c	Wed Jan 18 09:51:48 1995
@@ -205,9 +205,12 @@
 		case PROC_PID_CMDLINE:
 		case PROC_PID_STAT:
 		case PROC_PID_STATM:
-		case PROC_PID_MAPS:
 			inode->i_mode = S_IFREG | S_IRUGO;
 			inode->i_op = &proc_array_inode_operations;
+			return;
+		case PROC_PID_MAPS:
+			inode->i_mode = S_IFIFO | S_IRUGO;
+			inode->i_op = &proc_arraylong_inode_operations;
 			return;
 	}
 	switch (ino >> 8) {
diff -u --recursive --new-file v1.1.82/linux/fs/super.c linux/fs/super.c
--- v1.1.82/linux/fs/super.c	Mon Jan  2 14:20:58 1995
+++ linux/fs/super.c	Wed Jan 18 09:31:40 1995
@@ -17,6 +17,7 @@
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/locks.h>
+#include <linux/mm.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -517,15 +518,9 @@
 	if (!data)
 		return 0;
 
-	for (vma = current->mm->mmap ; ; ) {
-		if (!vma ||
-		    (unsigned long) data < vma->vm_start) {
-			return -EFAULT;
-		}
-		if ((unsigned long) data < vma->vm_end)
-			break;
-		vma = vma->vm_next;
-	}
+	vma = find_vma(current, (unsigned long) data);
+	if (!vma || (unsigned long) data < vma->vm_start)
+		return -EFAULT;
 	i = vma->vm_end - (unsigned long) data;
 	if (PAGE_SIZE <= (unsigned long) i)
 		i = PAGE_SIZE-1;
diff -u --recursive --new-file v1.1.82/linux/fs/sysv/file.c linux/fs/sysv/file.c
--- v1.1.82/linux/fs/sysv/file.c	Sat Nov  5 13:32:13 1994
+++ linux/fs/sysv/file.c	Wed Jan 18 09:51:48 1995
@@ -189,8 +189,10 @@
 	if (!read)
 		return -EIO;
 	filp->f_reada = 1;
-	if (!IS_RDONLY(inode))
+	if (!IS_RDONLY(inode)) {
 		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
 	return read;
 }
 
diff -u --recursive --new-file v1.1.82/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v1.1.82/linux/fs/sysv/inode.c	Wed Jan 11 21:14:28 1995
+++ linux/fs/sysv/inode.c	Wed Jan 18 09:51:48 1995
@@ -510,13 +510,9 @@
 		unsigned long old_time = *sb->sv_sb_time;
 		if (sb->sv_convert)
 			old_time = from_coh_ulong(old_time);
-		switch (sb->sv_type) {
-			case FSTYPE_SYSV4:
-				if (*sb->sv_sb_state == 0x7c269d38 - old_time)
-					*sb->sv_sb_state = 0x7c269d38 - time;
-			default:
-				break;
-		}
+		if (sb->sv_type == FSTYPE_SYSV4)
+			if (*sb->sv_sb_state == 0x7c269d38 - old_time)
+				*sb->sv_sb_state = 0x7c269d38 - time;
 		if (sb->sv_convert)
 			time = to_coh_ulong(time);
 		*sb->sv_sb_time = time;
diff -u --recursive --new-file v1.1.82/linux/fs/sysv/namei.c linux/fs/sysv/namei.c
--- v1.1.82/linux/fs/sysv/namei.c	Sat Nov  5 13:32:13 1994
+++ linux/fs/sysv/namei.c	Wed Jan 18 09:51:48 1995
@@ -184,6 +184,7 @@
 			}
 		} else {
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+			dir->i_dirt = 1;
 			for (i = 0; i < SYSV_NAMELEN ; i++)
 				de->name[i] = (i < namelen) ? name[i] : 0;
 			mark_buffer_dirty(bh, 1);
diff -u --recursive --new-file v1.1.82/linux/fs/xiafs/namei.c linux/fs/xiafs/namei.c
--- v1.1.82/linux/fs/xiafs/namei.c	Wed Jan 11 21:14:28 1995
+++ linux/fs/xiafs/namei.c	Wed Jan 18 09:51:48 1995
@@ -356,6 +356,7 @@
     inode->i_op = &xiafs_dir_inode_operations;
     inode->i_size = XIAFS_ZSIZE(dir->i_sb);
     inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+    inode->i_dirt = 1;
     dir_block = xiafs_bread(inode,0,1);
     if (!dir_block) {
         iput(dir);
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/bitops.h linux/include/asm-mips/bitops.h
--- v1.1.82/linux/include/asm-mips/bitops.h	Mon Jan 16 14:18:24 1995
+++ linux/include/asm-mips/bitops.h	Wed Jan 18 08:54:13 1995
@@ -5,14 +5,14 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (c) 1994 by Ralf Baechle
+ * Copyright (c) 1994, 1995  Ralf Baechle
  */
 #ifndef __ASM_MIPS_BITOPS_H
 #define __ASM_MIPS_BITOPS_H
 
-#include <asm/mipsregs.h>
+#ifdef __R4000__
 
-#if defined(__R4000__)
+#include <asm/mipsregs.h>
 
 /*
  * The following functions will only work for the R4000!
@@ -62,6 +62,90 @@
 	return retval;
 }
 
+#else /* !defined(__R4000__) */
+
+#include <asm/system.h>
+
+#ifdef __KERNEL__
+/*
+ * Only disable interrupt for kernelmode stuff to keep some
+ * usermode stuff alive
+ */
+#define __flags unsigned long flags
+#define __cli() cli()
+#define __save_flags(x) save_flags(x)
+#define __restore_flags(x) restore_flags(x)
+#endif /* __KERNEL__ */
+
+extern __inline__ int set_bit(int nr, void * addr)
+{
+	int	mask, retval;
+	int	*a = addr;
+	__flags;
+
+	a += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	__save_flags(flags);
+	__cli();
+	retval = (mask & *a) != 0;
+	*a |= mask;
+	__restore_flags(flags);
+
+	return retval;
+}
+
+extern __inline__ int clear_bit(int nr, void * addr)
+{
+	int	mask, retval;
+	int	*a = addr;
+	__flags;
+
+	a += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	__save_flags(flags);
+	__cli();
+	retval = (mask & *a) != 0;
+	*a &= ~mask;
+	__restore_flags(flags);
+
+	return retval;
+}
+
+extern __inline__ int change_bit(int nr, void * addr)
+{
+	int	mask, retval;
+	int	*a = addr;
+	__flags;
+
+	a += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	__save_flags(flags);
+	__cli();
+	retval = (mask & *a) != 0;
+	*a ^= mask;
+	__restore_flags(flags);
+
+	return retval;
+}
+
+#undef __flags
+#undef __cli()
+#undef __save_flags(x)
+#undef __restore_flags(x)
+
+#endif /* !defined(__R4000__) */
+
+extern __inline__ int test_bit(int nr, void *addr)
+{
+	int	mask;
+	unsigned long	*a;
+
+	a = addr;
+	addr += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	return ((mask & *a) != 0);
+}
+
 extern __inline__ int find_first_zero_bit (void *addr, unsigned size)
 {
 	int res;
@@ -86,38 +170,16 @@
 		".set\tat\n\t"
 		".set\treorder\n"
 		"2:"
-		: "=d" (res)
-		: "d" ((unsigned int) 0xffffffff),
-		  "d" (size),
+		: "=r" (res)
+		: "r" ((unsigned int) 0xffffffff),
+		  "r" (size),
 		  "0" ((signed int) 0),
-		  "d" (addr)
+		  "r" (addr)
 		: "$1");
 
 	return res;
 }
 
-#else /* !defined(__R4000__) */
-
-#define __USE_PORTABLE_STRINGS_H
-
-#define __USE_GENERIC_set_bit
-#define __USE_GENERIC_clear_bit
-#define __USE_GENERIC_change_bit
-#define __USE_GENERIC_find_first_zero_bit
-
-#endif /* !defined(__R4000__) */
-
-extern __inline__ int test_bit(int nr, void *addr)
-{
-	int	mask;
-	unsigned long	*a;
-
-	a = addr;
-	addr += nr >> 5;
-	mask = 1 << (nr & 0x1f);
-	return ((mask & *a) != 0);
-}
-
 extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
 {
 	unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
@@ -182,9 +244,5 @@
 
 	return __res;
 }
-
-#ifdef __USE_PORTABLE_BITOPS_H
-#include <asm-generic/bitops.h>
-#endif
 
 #endif /* __ASM_MIPS_BITOPS_H */
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/bootinfo.h linux/include/asm-mips/bootinfo.h
--- v1.1.82/linux/include/asm-mips/bootinfo.h	Mon Jan 16 14:18:24 1995
+++ linux/include/asm-mips/bootinfo.h	Wed Jan 18 08:54:13 1995
@@ -63,8 +63,11 @@
  * bss segment directly after startup.
  */
 
-struct bootinfo {
+struct drive_info_struct {
+	char dummy[32];
+	};
 
+struct bootinfo {
 	unsigned long machtype;			/* machine type */
 	unsigned long cputype;			/* system CPU & FPU */
 
@@ -100,6 +103,12 @@
 	 */
 	unsigned long ramdisk_size;		/* ramdisk size in 1024 byte blocks */
 	unsigned long ramdisk_base;		/* address of the ram disk in mem */
+
+	/*
+	 * Boot flags for the kernel
+	 */
+	unsigned long mount_root_rdonly;
+	struct drive_info_struct drive_info;
 
 	/*
 	 * Video ram info (not in tty.h)
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/mipsconfig.h linux/include/asm-mips/mipsconfig.h
--- v1.1.82/linux/include/asm-mips/mipsconfig.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/mipsconfig.h	Tue Jan 17 00:57:01 1995
@@ -13,7 +13,7 @@
 #define __ASM_MIPS_MIPS_CONFIG_H
 
 /*
- * This is the virtual adress to which all ports are being mapped.
+ * This is the virtual address to which all ports are being mapped.
  * Must be a value that can be load with a lui instruction.
  */
 #define PORT_BASE		0xe0000000
@@ -31,7 +31,7 @@
  * The virtual address where we'll map the pagetables
  * For a base address of 0xe3000000 this is 0xe338c000
  * For a base address of 0xe4000000 this is 0xe4390000
- * FIXME: Gas misscomputes the following expression!
+ * FIXME: Gas miscomputes the following expression!
 #define TLB_ROOT		(TLBMAP + (TLBMAP >> (12-2)))
  */
 #define TLB_ROOT		0xe4390000
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/mipsregs.h linux/include/asm-mips/mipsregs.h
--- v1.1.82/linux/include/asm-mips/mipsregs.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/mipsregs.h	Tue Jan 17 00:57:01 1995
@@ -94,7 +94,7 @@
 #define PFN(addr,pagesizeshift) (((addr) & ((1 << (pagesizeshift))-1)) << 6)
 
 /*
- * Macros to access the system control copprocessor
+ * Macros to access the system control coprocessor
  */
 #define read_32bit_cp0_register(source)                                        \
 ({ int __res;                                                                  \
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/mm.h linux/include/asm-mips/mm.h
--- v1.1.82/linux/include/asm-mips/mm.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/mm.h	Tue Jan 17 00:57:01 1995
@@ -6,7 +6,7 @@
 /*
  * Note that we shift the lower 32bits of each EntryLo[01] entry
  * 6 bits to the left. That way we can convert the PFN into the
- * physical address by a single 'and' operation and gain 6 aditional
+ * physical address by a single 'and' operation and gain 6 additional
  * bits for storing information which isn't present in a normal
  * MIPS page table.
  * I've also changed the naming of some bits so that they conform
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/page.h linux/include/asm-mips/page.h
--- v1.1.82/linux/include/asm-mips/page.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/page.h	Wed Jan 18 08:54:13 1995
@@ -1,9 +1,10 @@
 #ifndef __ASM_MIPS_PAGE_H
 #define __ASM_MIPS_PAGE_H
 
+#ifndef __ASSEMBLY__
+
 #include <linux/linkage.h>
 
-#ifndef __ASSEMBLY__
 #define invalidate()	tlbflush();
 extern asmlinkage void tlbflush(void);
 #endif
@@ -14,21 +15,89 @@
 #define PAGE_SIZE			(1UL << PAGE_SHIFT)
 #define PGDIR_SIZE			(1UL << PGDIR_SHIFT)
 
+#define PAGE_OFFSET	0
+#define MAP_NR(addr) ((addr) >> PAGE_SHIFT)
+#define MAP_PAGE_RESERVED (1<<15)
+
+typedef unsigned short mem_map_t;
+
+/*
+ * Note that we shift the lower 32bits of each EntryLo[01] entry
+ * 6 bits to the left. That way we can convert the PFN into the
+ * physical address by a single 'and' operation and gain 6 aditional
+ * bits for storing information which isn't present in a normal
+ * MIPS page table.
+ * I've also changed the naming of some bits so that they conform
+ * the i386 naming as much as possible.
+ * PAGE_USER isn't implemented in software yet.
+ */
+#define PAGE_PRESENT               (1<<0)   /* implemented in software */
+#define PAGE_COW                   (1<<1)   /* implemented in software */
+#define PAGE_DIRTY                 (1<<2)   /* implemented in software */
+#define PAGE_USER                  (1<<3)   /* implemented in software */
+#define PAGE_UNUSED1               (1<<4)   /* implemented in software */
+#define PAGE_UNUSED2               (1<<5)   /* implemented in software */
+#define PAGE_GLOBAL                (1<<6)
+#define PAGE_ACCESSED              (1<<7)   /* The MIPS valid bit      */
+#define PAGE_RW                    (1<<8)   /* The MIPS dirty bit      */
+#define CACHE_CACHABLE_NO_WA       (0<<9)
+#define CACHE_CACHABLE_WA          (1<<9)
+#define CACHE_UNCACHED             (2<<9)
+#define CACHE_CACHABLE_NONCOHERENT (3<<9)
+#define CACHE_CACHABLE_CE          (4<<9)
+#define CACHE_CACHABLE_COW         (5<<9)
+#define CACHE_CACHABLE_CUW         (6<<9)
+#define CACHE_MASK                 (7<<9)
+
+#define PAGE_PRIVATE    (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
+                         PAGE_COW | CACHE_CACHABLE_NO_WA)
+#define PAGE_SHARED     (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
+                         CACHE_CACHABLE_NO_WA)
+#define PAGE_COPY       (PAGE_PRESENT | PAGE_ACCESSED | PAGE_COW | \
+                         CACHE_CACHABLE_NO_WA)
+#define PAGE_READONLY   (PAGE_PRESENT | PAGE_ACCESSED | CACHE_CACHABLE_NO_WA)
+#define PAGE_TABLE      (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \
+                         CACHE_CACHABLE_NO_WA)
+
+#define PAGE_CHG_MASK (PAGE_MASK | PAGE_ACCESSED | PAGE_DIRTY | CACHE_MASK)
+
 #ifdef __KERNEL__
 
-			/* number of bits that fit into a memory pointer */
+/* page table for 0-4MB for everybody */
+extern unsigned long pg0[1024];
+
+/*
+ * BAD_PAGETABLE is used when we need a bogus page-table, while
+ * BAD_PAGE is used for a bogus page.
+ *
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern unsigned long __bad_page(void);
+extern unsigned long __bad_pagetable(void);
+extern unsigned long __zero_page(void);
+
+#define BAD_PAGETABLE __bad_pagetable()
+#define BAD_PAGE __bad_page()
+#define ZERO_PAGE __zero_page()
+
+/* number of bits that fit into a memory pointer */
 #define BITS_PER_PTR			(8*sizeof(unsigned long))
-			/* to mask away the intra-page address bits */
+
+/* to mask away the intra-page address bits */
 #define PAGE_MASK			(~(PAGE_SIZE-1))
-			/* to mask away the intra-page address bits */
+
+/* to mask away the intra-page address bits */
 #define PGDIR_MASK			(~(PGDIR_SIZE-1))
-			/* to align the pointer to the (next) page boundary */
+
+/* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)		(((addr)+PAGE_SIZE-1)&PAGE_MASK)
-			/* to align the pointer to a pointer address */
+
+/* to align the pointer to a pointer address */
 #define PTR_MASK			(~(sizeof(void*)-1))
 
-					/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */
-					/* 64-bit machines, beware!  SRB. */
+/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */
+/* 64-bit machines, beware!  SRB. */
 #define SIZEOF_PTR_LOG2			2
 
 /* to find an entry in a page-table-directory */
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/param.h linux/include/asm-mips/param.h
--- v1.1.82/linux/include/asm-mips/param.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-mips/param.h	Wed Jan 18 08:54:13 1995
@@ -0,0 +1,20 @@
+#ifndef __ASM_MIPS_PARAM_H
+#define __ASM_MIPS_PARAM_H
+
+#ifndef HZ
+#define HZ 100
+#endif
+
+#define EXEC_PAGESIZE	4096
+
+#ifndef NGROUPS
+#define NGROUPS		32
+#endif
+
+#ifndef NOGROUP
+#define NOGROUP		(-1)
+#endif
+
+#define MAXHOSTNAMELEN	64	/* max length of hostname */
+
+#endif /* __ASM_MIPS_PARAM_H */
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/ptrace.h linux/include/asm-mips/ptrace.h
--- v1.1.82/linux/include/asm-mips/ptrace.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/ptrace.h	Wed Jan 18 08:54:13 1995
@@ -1,7 +1,7 @@
 /*
  * linux/include/asm-mips/ptrace.h
  *
- * machine dependend structs and defines to help the user use
+ * machine dependent structs and defines to help the user use
  * the ptrace system call.
  */
 #ifndef __ASM_MIPS_PTRACE_H
@@ -11,7 +11,7 @@
  * use ptrace (3 or 6, pid, PT_EXCL, data); to read or write
  * the processes registers.
  *
- * This defines/structures corrospond to the register layout on stack -
+ * This defines/structures correspond to the register layout on stack -
  * if the order here is changed, it needs to be updated in
  * arch/mips/fork.c:copy_process, asm/mips/signal.c:do_signal,
  * asm-mips/ptrace.c, include/asm-mips/ptrace.h.
@@ -58,7 +58,6 @@
 	long orig_reg2;
 };
 
-#ifdef __KERNEL__
 /*
  * Does the process account for user or for system time?
  */
@@ -71,11 +70,5 @@
 #error "#define user_mode(regs) for R3000!"
 
 #endif /* !defined (__R4000__) */
-#endif /* __KERNEL */
-
-/*
- * This function computes the interrupt number from the stack frame
- */
-#define pt_regs2irq(p) ((int) ((struct pt_regs *)p)->interrupt)        
 
 #endif /* __ASM_MIPS_PTRACE_H */
diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/signal.h linux/include/asm-mips/signal.h
--- v1.1.82/linux/include/asm-mips/signal.h	Mon Jan 16 14:18:25 1995
+++ linux/include/asm-mips/signal.h	Wed Jan 18 08:54:13 1995
@@ -1,8 +1,6 @@
 #ifndef __ASM_MIPS_SIGNAL_H
 #define __ASM_MIPS_SIGNAL_H
 
-#ifdef __KERNEL__
-
 struct sigcontext_struct {
 	unsigned long	       sc_at, sc_v0, sc_v1, sc_a0, sc_a1, sc_a2, sc_a3;
 	unsigned long	sc_t0, sc_t1, sc_t2, sc_t3, sc_t4, sc_t5, sc_t6, sc_t7;
@@ -14,7 +12,5 @@
 
 	unsigned long	oldmask;
 };
-
-#endif
 
 #endif /* __ASM_MIPS_SIGNAL_H */
diff -u --recursive --new-file v1.1.82/linux/include/linux/aztcd.h linux/include/linux/aztcd.h
--- v1.1.82/linux/include/linux/aztcd.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/aztcd.h	Wed Jan 18 21:15:26 1995
@@ -0,0 +1,124 @@
+/* $Id$
+ * Definitions for a AztechCD268 CD-ROM interface
+ *	Copyright (C) 1994, 1995  Werner Zimmermann
+ *
+ *	based on Mitsumi CDROM driver by Martin Harriss
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  History:	W.Zimmermann adaption to Aztech CD268-01A Version 1.3
+ *		Oktober 1994 Email: zimmerma@rz.fht-esslingen.de
+ *  Note:	Points marked with ??? are questionable !
+ */
+
+/* *** change this to set the I/O port address */
+#define AZT_BASE_ADDR		0x320
+
+/* Increase this if you get lots of timeouts; if you get kernel panic, replace
+   STEN_LOW_WAIT by STEN_LOW in the source code */
+#define AZT_STATUS_DELAY	400       /*for timer wait, STEN_LOW_WAIT*/
+#define AZT_TIMEOUT		8000000   /*for busy wait STEN_LOW, DTEN_LOW*/
+#define AZT_FAST_TIMEOUT	10000     /*for reading the version string*/
+
+/* number of times to retry a command before giving up */
+#define AZT_RETRY_ATTEMPTS	3
+
+/*defines for compatibility with mcd.c/mcd.h for Mitsumi drive, will probably
+  go away, when the AZTECH driver is integrated in the standard Linux kernel*/
+#ifdef CONFIG_AZTCD
+#else
+#define AZTCD_TIMER		    MCD_TIMER
+#define aztcd_init		    mcd_init
+#define do_aztcd_request	    do_mcd_request
+#define aztcd_setup		    mcd_setup
+#define check_aztcd_media_change    check_mcd_media_change
+#endif
+
+/* port access macros */
+#define CMD_PORT		azt_port
+#define DATA_PORT		azt_port
+#define STATUS_PORT		azt_port+1
+#define MODE_PORT		azt_port+2
+
+/* status bits */
+#define AST_CMD_CHECK		0x80		/* command error */
+#define AST_DSK_CHG		0x20		/* disk removed or changed */
+#define AST_NOT_READY		0x02		/* no disk in the drive */
+#define AST_DOOR_OPEN		0x40		/* door is open */
+#define AST_MODE_BITS		0x1C		/* Mode Bits */
+#define AST_INITIAL		0x0C		/* initial, only valid ... */
+#define AST_BUSY		0x04		/* now playing, only valid
+						   in combination with mode
+						   bits */
+/* flag bits */
+#define AFL_DATA		0x02		/* data available if low */
+#define AFL_STATUS		0x04		/* status available if low */
+#define AFL_OP_OK		0x01		/* OP_OK command correct*/
+#define AFL_PA_OK		0x02		/* PA_OK parameter correct*/
+#define AFL_OP_ERR		0x05		/* error in command*/
+#define AFL_PA_ERR		0x06		/* error in parameters*/
+#define POLLED			0x04		/* polled mode */
+
+/* commands */
+#define ACMD_SOFT_RESET		0x10		/* reset drive */
+#define ACMD_PLAY_READ		0x20		/* read data track in cooked mode */
+#define ACMD_DATA_READ_RAW      0x21		/* reading in raw mode*/
+#define ACMD_SEEK_TO_LEADIN     0x31		/* seek to leadin track*/
+#define ACMD_GET_ERROR		0x40		/* get error code */
+#define ACMD_GET_STATUS		0x41		/* get status */
+#define ACMD_GET_Q_CHANNEL      0x50		/* read info from q channel */
+#define ACMD_EJECT		0x60		/* eject/open */
+#define ACMD_PAUSE		0x80		/* pause */
+#define ACMD_STOP		0x81		/* stop play */
+#define ACMD_PLAY_AUDIO		0x90		/* play audio track */
+#define ACMD_GET_VERSION	0xA0		/* get firmware version */
+#define ACMD_SET_MODE		0xA1		/* set drive mode */
+#define ACMD_SET_VOLUME		0xAE		/* set audio level */
+
+/* borrowed from hd.c */
+#define SET_TIMER(func, jifs) \
+	((timer_table[AZTCD_TIMER].expires = jiffies + jifs), \
+	(timer_table[AZTCD_TIMER].fn = func), \
+	(timer_active |= 1<<AZTCD_TIMER))
+
+#define CLEAR_TIMER		timer_active &= ~(1<<AZTCD_TIMER)
+
+#define MAX_TRACKS		104
+
+struct msf {
+	unsigned char	min;
+	unsigned char	sec;
+	unsigned char	frame;
+};
+
+struct azt_Play_msf {
+	struct msf	start;
+	struct msf	end;
+};
+
+struct azt_DiskInfo {
+	unsigned char	first;
+	unsigned char	last;
+	struct msf	diskLength;
+	struct msf	firstTrack;
+};
+
+struct azt_Toc {
+	unsigned char	ctrl_addr;
+	unsigned char	track;
+	unsigned char	pointIndex;
+	struct msf	trackTime;
+	struct msf	diskTime;
+};
diff -u --recursive --new-file v1.1.82/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v1.1.82/linux/include/linux/cdrom.h	Mon Dec 19 17:26:24 1994
+++ linux/include/linux/cdrom.h	Wed Jan 18 07:51:45 1995
@@ -335,9 +335,8 @@
 	};
 
 /*
- * preliminary extension for obtaining multi session info
- * (still may change if other drivers will use it, too):
- * this has to be the "arg" of the CDROMMULTISESSION ioctl.
+ * this has to be the "arg" of the CDROMMULTISESSION ioctl
+ * for obtaining multi session info.
  * The returned "addr" is valid only if "xa_flag" is true.
  */
 struct cdrom_multisession
@@ -411,12 +410,8 @@
 #define	CDROMEJECT_SW		0x530f		/* arg: 0 or 1 */
  
 /*
- * preliminary extension for obtaining the start-of-last-session
- * address of multi session disks
- * currently used by sbpcd.c
- * (still may change if more drivers will use it).
+ * obtain the start-of-last-session address of multi session disks
  */
-#define	CDROMMULTISESSION_SYS	0x5310 /* internal use only (linux/fs/isofs/inode.c) */
-#define	CDROMMULTISESSION	0x5311 /* "user" interface (arg has to be a "cdrom_multisession" struct) */
+#define	CDROMMULTISESSION	0x5310 /* (struct cdrom_multisession) */
 
 #endif  _LINUX_CDROM_H
diff -u --recursive --new-file v1.1.82/linux/include/linux/major.h linux/include/linux/major.h
--- v1.1.82/linux/include/linux/major.h	Sun Jan  1 16:28:20 1995
+++ linux/include/linux/major.h	Wed Jan 18 21:00:26 1995
@@ -46,6 +46,7 @@
  * 26 -                        matsushita cdrom 2     minors 0..3
  * 27 - qic117 tape            matsushita cdrom 3     minors 0..3
  * 28 -                        matsushita cdrom 4     minors 0..3
+ * 29 -                        aztech/orchid/okano/wearnes cdrom
  */
 
 #define UNNAMED_MAJOR	0
@@ -78,6 +79,7 @@
 #define MATSUSHITA_CDROM3_MAJOR 27
 #define MATSUSHITA_CDROM4_MAJOR 28
 #define QIC117_TAPE_MAJOR 27
+#define AZTECH_CDROM_MAJOR 29
 
 /*
  * Tests for SCSI devices.
diff -u --recursive --new-file v1.1.82/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.1.82/linux/include/linux/mm.h	Mon Jan 16 14:18:25 1995
+++ linux/include/linux/mm.h	Wed Jan 18 09:31:39 1995
@@ -36,8 +36,12 @@
 	unsigned short vm_flags;
 /* linked list of VM areas per task, sorted by address */
 	struct vm_area_struct * vm_next;
+/* AVL tree of VM areas per task, sorted by address */
+	struct vm_area_struct * vm_avl_left;
+	struct vm_area_struct * vm_avl_right;
+	short vm_avl_height;
 /* for areas with inode, the circular list inode->i_mmap */
-/* for shm areas, the linked list of attaches */
+/* for shm areas, the circular list of attaches */
 /* otherwise unused */
 	struct vm_area_struct * vm_next_share;
 	struct vm_area_struct * vm_prev_share;
@@ -192,9 +196,13 @@
 /* mmap.c */
 extern int do_mmap(struct file * file, unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long off);
-extern void merge_segments(struct vm_area_struct *);
+extern struct vm_area_struct * find_vma (struct task_struct *, unsigned long);
+extern struct vm_area_struct * find_vma_intersection (struct task_struct *, unsigned long, unsigned long);
+extern void merge_segments(struct task_struct *, unsigned long, unsigned long);
 extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *);
 extern void remove_shared_vm_struct(struct vm_area_struct *);
+extern void build_mmap_avl(struct task_struct *);
+extern void exit_mmap(struct task_struct *);
 extern int do_munmap(unsigned long, size_t);
 extern unsigned long get_unmapped_area(unsigned long);
 
diff -u --recursive --new-file v1.1.82/linux/include/linux/pci.h linux/include/linux/pci.h
--- v1.1.82/linux/include/linux/pci.h	Mon Jan 16 14:18:25 1995
+++ linux/include/linux/pci.h	Tue Jan 17 00:57:01 1995
@@ -360,7 +360,7 @@
 	{0xff,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82378,	"82378IB"}, \
 	{0x00,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82424,	"82424ZX Saturn"}, \
 	{0xff,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82375,	"82375EB"}, \
-	{0x00,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82434,	"82434LX Mercury/Netpune"}, \
+	{0x00,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82434,	"82434LX Mercury/Neptune"}, \
 	{0xff,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82430,	"82430ZX Aries"}, \
 	{0xff,	PCI_VENDOR_ID_SMC,	PCI_DEVICE_ID_SMC_37C665,	"FDC 37C665"}, \
 	{0xff,	PCI_VENDOR_ID_ATI,	PCI_DEVICE_ID_ATI_M32,		"Mach 32"}, \
@@ -385,7 +385,7 @@
 }
 
 /* An item of this structure has the following meaning	*/
-/* For each optimisation, the register adress, the mask	*/
+/* For each optimisation, the register address, the mask	*/
 /* and value to write to turn it on.			*/
 /* There are 5 optimizations for the moment :		*/
 /* Cache L2 write back best than write through		*/
@@ -414,7 +414,7 @@
 }
 
 struct bridge_mapping_type {
-	unsigned char	adress;
+	unsigned char	address;
 	unsigned char	mask;
 	unsigned char	value;
 };
@@ -430,7 +430,7 @@
 /*	This is a dummy entry for my tests.	*/
 /*	I have this chipset and no docs....	*/   
 
-/* I'am gathering docs. If you can help......	*/
+/* I'm gathering docs. If you can help......	*/
 
 #define BRIDGE_MAPPING_NUM 3
 #define BRIDGE_MAPPING_TYPE { \
diff -u --recursive --new-file v1.1.82/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.1.82/linux/include/linux/proc_fs.h	Wed Jan 11 21:14:29 1995
+++ linux/include/linux/proc_fs.h	Wed Jan 18 09:51:48 1995
@@ -113,6 +113,7 @@
 extern struct inode_operations proc_net_inode_operations;
 extern struct inode_operations proc_mem_inode_operations;
 extern struct inode_operations proc_array_inode_operations;
+extern struct inode_operations proc_arraylong_inode_operations;
 extern struct inode_operations proc_kcore_inode_operations;
 extern struct inode_operations proc_profile_inode_operations;
 extern struct inode_operations proc_kmsg_inode_operations;
diff -u --recursive --new-file v1.1.82/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.1.82/linux/include/linux/sched.h	Mon Jan 16 14:18:26 1995
+++ linux/include/linux/sched.h	Wed Jan 18 09:31:40 1995
@@ -120,6 +120,7 @@
 	unsigned long dec_flt;		/* page fault count of the last time */
 	unsigned long swap_cnt;		/* number of pages to swap on next pass */
 	struct vm_area_struct * mmap;
+	struct vm_area_struct * mmap_avl;
 };
 
 #define INIT_MMAP { &init_task, 0, 0x40000000, PAGE_SHARED, }
@@ -133,7 +134,7 @@
 /* ?_flt */	0, 0, 0, 0, \
 		0, \
 /* swap */	0, 0, 0, 0, \
-		&init_mmap }
+		&init_mmap, &init_mmap }
 
 struct task_struct {
 /* these are hardcoded - don't touch */
diff -u --recursive --new-file v1.1.82/linux/include/linux/timer.h linux/include/linux/timer.h
--- v1.1.82/linux/include/linux/timer.h	Mon Jul 25 12:23:03 1994
+++ linux/include/linux/timer.h	Wed Jan 18 21:04:04 1995
@@ -30,6 +30,8 @@
  * QIC02_TAPE_TIMER	timer for QIC-02 tape driver (it's not hardcoded)
  *
  * MCD_TIMER		Mitsumi CD-ROM Timer
+ *
+ * AZTCD_TIMER		Aztech CD-ROM Timer
  */
 
 #define BLANK_TIMER	0
@@ -47,6 +49,7 @@
 #define MCD_TIMER	23
 
 #define HD_TIMER2	24
+#define AZTCD_TIMER     25
 
 struct timer_struct {
 	unsigned long expires;
diff -u --recursive --new-file v1.1.82/linux/init/main.c linux/init/main.c
--- v1.1.82/linux/init/main.c	Mon Jan 16 14:18:26 1995
+++ linux/init/main.c	Wed Jan 18 22:52:36 1995
@@ -85,6 +85,7 @@
 extern void xd_setup(char *str, int *ints);
 extern void floppy_setup(char *str, int *ints);
 extern void mcd_setup(char *str, int *ints);
+extern void aztcd_setup(char *str, int *ints);
 extern void st_setup(char *str, int *ints);
 extern void st0x_setup(char *str, int *ints);
 extern void tmc8xx_setup(char *str, int *ints);
@@ -217,6 +218,9 @@
 #ifdef CONFIG_MCD
 	{ "mcd=", mcd_setup },
 #endif
+#ifdef CONFIG_AZTCD
+	{ "aztcd=", aztcd_setup },
+#endif
 #ifdef CONFIG_SOUND
 	{ "sound=", sound_setup },
 #endif
@@ -385,7 +389,6 @@
 	memory_start = kmalloc_init(memory_start,memory_end);
 	sti();
 	calibrate_delay();
-	cli();
 	memory_start = chr_dev_init(memory_start,memory_end);
 	memory_start = blk_dev_init(memory_start,memory_end);
 	sti();
diff -u --recursive --new-file v1.1.82/linux/ipc/shm.c linux/ipc/shm.c
--- v1.1.82/linux/ipc/shm.c	Fri Jan 13 10:12:24 1995
+++ linux/ipc/shm.c	Wed Jan 18 09:31:40 1995
@@ -369,6 +369,44 @@
 	shm_swap_in		/* swapin */
 };
 
+/* Insert shmd into the circular list shp->attaches */
+static inline void insert_attach (struct shmid_ds * shp, struct vm_area_struct * shmd)
+{
+	struct vm_area_struct * attaches;
+
+	if ((attaches = shp->attaches)) {
+		shmd->vm_next_share = attaches;
+		shmd->vm_prev_share = attaches->vm_prev_share;
+		shmd->vm_prev_share->vm_next_share = shmd;
+		attaches->vm_prev_share = shmd;
+	} else
+		shp->attaches = shmd->vm_next_share = shmd->vm_prev_share = shmd;
+}
+
+/* Remove shmd from circular list shp->attaches */
+static inline void remove_attach (struct shmid_ds * shp, struct vm_area_struct * shmd)
+{
+	if (shmd->vm_next_share == shmd) {
+		if (shp->attaches != shmd) {
+			printk("shm_close: shm segment (id=%ld) attach list inconsistent\n",
+				(shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK);
+			printk("shm_close: %d %08lx-%08lx %c%c%c%c %08lx %08lx\n",
+				shmd->vm_task->pid, shmd->vm_start, shmd->vm_end,
+				shmd->vm_flags & VM_READ ? 'r' : '-',
+				shmd->vm_flags & VM_WRITE ? 'w' : '-',
+				shmd->vm_flags & VM_EXEC ? 'x' : '-',
+				shmd->vm_flags & VM_SHARED ? 's' : 'p',
+				shmd->vm_offset, shmd->vm_pte);
+		}
+		shp->attaches = NULL;
+	} else {
+		if (shp->attaches == shmd)
+			shp->attaches = shmd->vm_next_share;
+		shmd->vm_prev_share->vm_next_share = shmd->vm_next_share;
+		shmd->vm_next_share->vm_prev_share = shmd->vm_prev_share;
+	}
+}
+
 /*
  * check range is unmapped, ensure page tables exist
  * mark page table entries with shm_sgn.
@@ -398,7 +436,7 @@
 
 	/* add new mapping */
 	insert_vm_struct(current, shmd);
-	merge_segments(current->mm->mmap);
+	merge_segments(current, shmd->vm_start, shmd->vm_end);
 
 	/* check that the range has page_tables */
 	for (tmp = shmd->vm_start; tmp < shmd->vm_end; tmp += PAGE_SIZE) {
@@ -475,12 +513,11 @@
 		return -EINVAL;
 	}
 	if (!(shmflg & SHM_REMAP))
-		for (shmd = current->mm->mmap; shmd; shmd = shmd->vm_next)
-			if (!(addr >= shmd->vm_end || addr + shp->shm_segsz <= shmd->vm_start)) {
-				/* printk("shmat() -> EINVAL because the interval [0x%lx,0x%lx) intersects an already mapped interval [0x%lx,0x%lx).\n",
-					addr, addr + shp->shm_segsz, shmd->vm_start, shmd->vm_end); */
-				return -EINVAL;
-			}
+		if ((shmd = find_vma_intersection(current, addr, addr + shp->shm_segsz))) {
+			/* printk("shmat() -> EINVAL because the interval [0x%lx,0x%lx) intersects an already mapped interval [0x%lx,0x%lx).\n",
+				addr, addr + shp->shm_segsz, shmd->vm_start, shmd->vm_end); */
+			return -EINVAL;
+		}
 
 	if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO))
 		return -EACCES;
@@ -503,7 +540,7 @@
 	shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
 			 | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
 			 | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);
-	shmd->vm_next_share = NULL;
+	shmd->vm_next_share = shmd->vm_prev_share = NULL;
 	shmd->vm_inode = NULL;
 	shmd->vm_offset = 0;
 	shmd->vm_ops = &shm_vm_ops;
@@ -516,8 +553,8 @@
 		return err;
 	}
 
-	shmd->vm_next_share = shp->attaches;
-	shp->attaches = shmd;
+	insert_attach(shp,shmd);  /* insert shmd into shp->attaches */
+
 	shp->shm_lpid = current->pid;
 	shp->shm_atime = CURRENT_TIME;
 
@@ -537,8 +574,7 @@
 		printk("shm_open: unused id=%d PANIC\n", id);
 		return;
 	}
-	shmd->vm_next_share = shp->attaches;
-	shp->attaches = shmd;
+	insert_attach(shp,shmd);  /* insert shmd into shp->attaches */
 	shp->shm_nattch++;
 	shp->shm_atime = CURRENT_TIME;
 	shp->shm_lpid = current->pid;
@@ -552,7 +588,6 @@
  */
 static void shm_close (struct vm_area_struct *shmd)
 {
-	struct vm_area_struct **shmdp;
 	struct shmid_ds *shp;
 	int id;
 
@@ -561,21 +596,7 @@
 	/* remove from the list of attaches of the shm segment */
 	id = (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK;
 	shp = shm_segs[id];
-	for (shmdp = &shp->attaches; *shmdp; shmdp = &(*shmdp)->vm_next_share)
-		if (*shmdp == shmd) {
-			*shmdp = shmd->vm_next_share;
-			goto found;
-		}
-	printk("shm_close: shm segment (id=%d) attach list inconsistent\n",id);
-	printk("shm_close: %d %08lx-%08lx %c%c%c%c %08lx %08lx\n",
-		shmd->vm_task->pid, shmd->vm_start, shmd->vm_end,
-		shmd->vm_flags & VM_READ ? 'r' : '-',
-		shmd->vm_flags & VM_WRITE ? 'w' : '-',
-		shmd->vm_flags & VM_EXEC ? 'x' : '-',
-		shmd->vm_flags & VM_SHARED ? 's' : 'p',
-		shmd->vm_offset, shmd->vm_pte);
-
- found:
+	remove_attach(shp,shmd);  /* remove from shp->attaches */
   	shp->shm_lpid = current->pid;
 	shp->shm_dtime = CURRENT_TIME;
 	if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
@@ -714,7 +735,8 @@
 		swap_free (swap_nr);
 		return 0;
 	}
-	for (shmd = shp->attaches; shmd; shmd = shmd->vm_next_share) {
+	for (shmd = shp->attaches; ; ) {
+	    do {
 		unsigned long tmp, *pte;
 		if ((shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK) != id) {
 			printk ("shm_swap: id=%ld does not match shmd->vm_pte.id=%ld\n", id, shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK);
@@ -743,6 +765,10 @@
 		mem_map[MAP_NR(page)]--;
 		shmd->vm_task->mm->rss--;
 		invalid++;
+	    /* continue looping through circular list */
+	    } while (0);
+	    if ((shmd = shmd->vm_next_share) == shp->attaches)
+		break;
 	}
 
 	if (mem_map[MAP_NR(page)] != 1)
diff -u --recursive --new-file v1.1.82/linux/kernel/exit.c linux/kernel/exit.c
--- v1.1.82/linux/kernel/exit.c	Mon Jan  9 07:22:11 1995
+++ linux/kernel/exit.c	Wed Jan 18 09:31:39 1995
@@ -354,26 +354,6 @@
 	}
 }
 
-static void exit_mm(void)
-{
-	struct vm_area_struct * mpnt;
-
-	mpnt = current->mm->mmap;
-	current->mm->mmap = NULL;
-	while (mpnt) {
-		struct vm_area_struct * next = mpnt->vm_next;
-		if (mpnt->vm_ops && mpnt->vm_ops->close)
-			mpnt->vm_ops->close(mpnt);
-		remove_shared_vm_struct(mpnt);
-		if (mpnt->vm_inode)
-			iput(mpnt->vm_inode);
-		kfree(mpnt);
-		mpnt = next;
-	}
-
-	free_page_tables(current);
-}
-
 static void exit_files(void)
 {
 	int i;
@@ -402,7 +382,8 @@
 fake_volatile:
 	if (current->semundo)
 		sem_exit();
-	exit_mm();
+	exit_mmap(current);
+	free_page_tables(current);
 	exit_files();
 	exit_fs();
 	exit_thread();
diff -u --recursive --new-file v1.1.82/linux/kernel/fork.c linux/kernel/fork.c
--- v1.1.82/linux/kernel/fork.c	Fri Jan 13 10:12:24 1995
+++ linux/kernel/fork.c	Wed Jan 18 09:31:40 1995
@@ -93,8 +93,10 @@
 	p = &tsk->mm->mmap;
 	for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) {
 		tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
-		if (!tmp)
+		if (!tmp) {
+			exit_mmap(tsk);
 			return -ENOMEM;
+		}
 		*tmp = *mpnt;
 		tmp->vm_task = tsk;
 		tmp->vm_next = NULL;
@@ -110,6 +112,7 @@
 		*p = tmp;
 		p = &tmp->vm_next;
 	}
+	build_mmap_avl(tsk);
 	return 0;
 }
 
diff -u --recursive --new-file v1.1.82/linux/kernel/sys.c linux/kernel/sys.c
--- v1.1.82/linux/kernel/sys.c	Mon Jan  9 07:22:12 1995
+++ linux/kernel/sys.c	Wed Jan 18 09:31:40 1995
@@ -16,6 +16,7 @@
 #include <linux/ptrace.h>
 #include <linux/stat.h>
 #include <linux/mman.h>
+#include <linux/mm.h>
 
 #include <asm/segment.h>
 #include <asm/io.h>
@@ -380,7 +381,6 @@
 	int freepages;
 	unsigned long rlim;
 	unsigned long newbrk, oldbrk;
-	struct vm_area_struct * vma;
 
 	if (brk < current->mm->end_code)
 		return current->mm->brk;
@@ -409,12 +409,8 @@
 	/*
 	 * Check against existing mmap mappings.
 	 */
-	for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
-		if (newbrk <= vma->vm_start)
-			break;
-		if (oldbrk < vma->vm_end)
-			return current->mm->brk;
-	}
+	if (find_vma_intersection(current, oldbrk, newbrk))
+		return current->mm->brk;
 	/*
 	 * stupid algorithm to decide if we have enough memory: while
 	 * simple, it hopefully works in most obvious cases.. Easy to
diff -u --recursive --new-file v1.1.82/linux/kernel/time.c linux/kernel/time.c
--- v1.1.82/linux/kernel/time.c	Mon Jan  9 07:22:12 1995
+++ linux/kernel/time.c	Wed Jan 18 08:54:13 1995
@@ -203,16 +203,19 @@
 
 	save_flags(flags);
 	cli();
-#ifdef __i386__
+#if defined (__i386__) || defined (__mips__)
 	*tv = xtime;
 	tv->tv_usec += do_gettimeoffset();
 	if (tv->tv_usec >= 1000000) {
 		tv->tv_usec -= 1000000;
 		tv->tv_sec++;
 	}
-#else /* not __i386__ */
+	sti();
+#else /* !defined (__i386__) && !defined (__mips__) */
+	cli();
 	*tv = xtime;
-#endif /* not __i386__ */
+	sti();
+#endif /* !defined (__i386__) && !defined (__mips__) */
 	restore_flags(flags);
 }
 
diff -u --recursive --new-file v1.1.82/linux/lib/string.c linux/lib/string.c
--- v1.1.82/linux/lib/string.c	Mon Jan 16 14:18:33 1995
+++ linux/lib/string.c	Wed Jan 18 10:01:26 1995
@@ -67,7 +67,7 @@
 	register char __res;
 
 	while (1) {
-		if ((__res = *cs - *ct++) != 0 && *cs++)
+		if ((__res = *cs - *ct++) != 0 || !*cs++)
 			break;
 	}
 
@@ -190,10 +190,20 @@
 
 void * memmove(void * dest,const void *src,size_t count)
 {
-	char *tmp = (char *) dest, *s = (char *) src;
+	char *tmp, *s;
 
-	while (count--)
-		*tmp++ = *s++;
+	if (dest <= src) {
+		tmp = (char *) dest;
+		s = (char *) src;
+		while (count--)
+			*tmp++ = *s++;
+		}
+	else {
+		tmp = (char *) dest + count;
+		s = (char *) src + count;
+		while (count--)
+			*--tmp = *--s;
+		}
 
 	return dest;
 }
diff -u --recursive --new-file v1.1.82/linux/mm/memory.c linux/mm/memory.c
--- v1.1.82/linux/mm/memory.c	Mon Jan 16 14:18:33 1995
+++ linux/mm/memory.c	Wed Jan 18 09:31:40 1995
@@ -615,12 +615,9 @@
 	if (get_fs() == get_ds())
 		return 0;
 
-	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			goto bad_area;
-		if (vma->vm_end > start)
-			break;
-	}
+	vma = find_vma(current, start);
+	if (!vma)
+		goto bad_area;
 	if (vma->vm_start <= start)
 		goto good_area;
 	if (!(vma->vm_flags & VM_GROWSDOWN))
diff -u --recursive --new-file v1.1.82/linux/mm/mmap.c linux/mm/mmap.c
--- v1.1.82/linux/mm/mmap.c	Thu Dec  1 11:48:12 1994
+++ linux/mm/mmap.c	Wed Jan 18 09:31:40 1995
@@ -73,7 +73,7 @@
 		}
 		if ((flags & MAP_DENYWRITE) && (file->f_inode->i_wcount > 0))
 			return -ETXTBSY;
-	} else if ((flags & MAP_TYPE) == MAP_SHARED)
+	} else if ((flags & MAP_TYPE) != MAP_PRIVATE)
 		return -EINVAL;
 
 	/*
@@ -147,7 +147,7 @@
 		return error;
 	}
 	insert_vm_struct(current, vma);
-	merge_segments(current->mm->mmap);
+	merge_segments(current, vma->vm_start, vma->vm_end);
 	return addr;
 }
 
@@ -195,6 +195,428 @@
 		get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
 }
 
+
+/*
+ * Searching a VMA in the linear list task->mm->mmap is horribly slow.
+ * Use an AVL (Adelson-Velskii and Landis) tree to speed up this search
+ * from O(n) to O(log n), where n is the number of VMAs of the task
+ * (typically around 6, but may reach 3000 in some cases).
+ * Written by Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>.
+ */
+
+/* We keep the list and tree sorted by address. */
+#define vm_avl_key	vm_end
+#define vm_avl_key_t	unsigned long	/* typeof(vma->avl_key) */
+
+/*
+ * task->mm->mmap_avl is the AVL tree corresponding to task->mm->mmap
+ * or, more exactly, its root.
+ * A vm_area_struct has the following fields:
+ *   vm_avl_left     left son of a tree node
+ *   vm_avl_right    right son of a tree node
+ *   vm_avl_height   1+max(heightof(left),heightof(right))
+ * The empty tree is represented as NULL.
+ */
+#define avl_empty	(struct vm_area_struct *) NULL
+
+/* Since the trees are balanced, their height will never be large. */
+#define avl_maxheight	41	/* why this? a small exercise */
+#define heightof(tree)	((tree) == avl_empty ? 0 : (tree)->vm_avl_height)
+/*
+ * Consistency and balancing rules:
+ * 1. tree->vm_avl_height == 1+max(heightof(tree->vm_avl_left),heightof(tree->vm_avl_right))
+ * 2. abs( heightof(tree->vm_avl_left) - heightof(tree->vm_avl_right) ) <= 1
+ * 3. foreach node in tree->vm_avl_left: node->vm_avl_key <= tree->vm_avl_key,
+ *    foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key.
+ */
+
+/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
+struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
+{
+#if 0 /* equivalent, but slow */
+	struct vm_area_struct * vma;
+
+	for (vma = task->mm->mmap ; ; vma = vma->vm_next) {
+		if (!vma)
+			return NULL;
+		if (vma->vm_end > addr)
+			return vma;
+	}
+#else
+	struct vm_area_struct * result = NULL;
+	struct vm_area_struct * tree;
+
+	for (tree = task->mm->mmap_avl ; ; ) {
+		if (tree == avl_empty)
+			return result;
+		if (tree->vm_end > addr) {
+			if (tree->vm_start <= addr)
+				return tree;
+			result = tree;
+			tree = tree->vm_avl_left;
+		} else
+			tree = tree->vm_avl_right;
+	}
+#endif
+}
+
+/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
+   NULL if none.  Assume start_addr < end_addr. */
+struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
+{
+	struct vm_area_struct * vma;
+
+#if 0 /* equivalent, but slow */
+	for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
+		if (end_addr <= vma->vm_start)
+			break;
+		if (start_addr < vma->vm_end)
+			return vma;
+	}
+	return NULL;
+#else
+	vma = find_vma(task,start_addr);
+	if (!vma || end_addr <= vma->vm_start)
+		return NULL;
+	return vma;
+#endif
+}
+
+/* Look up the nodes at the left and at the right of a given node. */
+static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+{
+	vm_avl_key_t key = node->vm_avl_key;
+
+	*to_the_left = *to_the_right = NULL;
+	for (;;) {
+		if (tree == avl_empty) {
+			printk("avl_neighbours: node not found in the tree\n");
+			return;
+		}
+		if (key == tree->vm_avl_key)
+			break;
+		if (key < tree->vm_avl_key) {
+			*to_the_right = tree;
+			tree = tree->vm_avl_left;
+		} else {
+			*to_the_left = tree;
+			tree = tree->vm_avl_right;
+		}
+	}
+	if (tree != node) {
+		printk("avl_neighbours: node not exactly found in the tree\n");
+		return;
+	}
+	if (tree->vm_avl_left != avl_empty) {
+		struct vm_area_struct * node;
+		for (node = tree->vm_avl_left; node->vm_avl_right != avl_empty; node = node->vm_avl_right)
+			continue;
+		*to_the_left = node;
+	}
+	if (tree->vm_avl_right != avl_empty) {
+		struct vm_area_struct * node;
+		for (node = tree->vm_avl_right; node->vm_avl_left != avl_empty; node = node->vm_avl_left)
+			continue;
+		*to_the_right = node;
+	}
+	if ((*to_the_left && ((*to_the_left)->vm_next != node)) || (node->vm_next != *to_the_right))
+		printk("avl_neighbours: tree inconsistent with list\n");
+}
+
+/*
+ * Rebalance a tree.
+ * After inserting or deleting a node of a tree we have a sequence of subtrees
+ * nodes[0]..nodes[k-1] such that
+ * nodes[0] is the root and nodes[i+1] = nodes[i]->{vm_avl_left|vm_avl_right}.
+ */
+static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
+{
+	for ( ; count > 0 ; count--) {
+		struct vm_area_struct ** nodeplace = *--nodeplaces_ptr;
+		struct vm_area_struct * node = *nodeplace;
+		struct vm_area_struct * nodeleft = node->vm_avl_left;
+		struct vm_area_struct * noderight = node->vm_avl_right;
+		int heightleft = heightof(nodeleft);
+		int heightright = heightof(noderight);
+		if (heightright + 1 < heightleft) {
+			/*                                                      */
+			/*                            *                         */
+			/*                          /   \                       */
+			/*                       n+2      n                     */
+			/*                                                      */
+			struct vm_area_struct * nodeleftleft = nodeleft->vm_avl_left;
+			struct vm_area_struct * nodeleftright = nodeleft->vm_avl_right;
+			int heightleftright = heightof(nodeleftright);
+			if (heightof(nodeleftleft) >= heightleftright) {
+				/*                                                        */
+				/*                *                    n+2|n+3            */
+				/*              /   \                  /    \             */
+				/*           n+2      n      -->      /   n+1|n+2         */
+				/*           / \                      |    /    \         */
+				/*         n+1 n|n+1                 n+1  n|n+1  n        */
+				/*                                                        */
+				node->vm_avl_left = nodeleftright; nodeleft->vm_avl_right = node;
+				nodeleft->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightleftright);
+				*nodeplace = nodeleft;
+			} else {
+				/*                                                        */
+				/*                *                     n+2               */
+				/*              /   \                 /     \             */
+				/*           n+2      n      -->    n+1     n+1           */
+				/*           / \                    / \     / \           */
+				/*          n  n+1                 n   L   R   n          */
+				/*             / \                                        */
+				/*            L   R                                       */
+				/*                                                        */
+				nodeleft->vm_avl_right = nodeleftright->vm_avl_left;
+				node->vm_avl_left = nodeleftright->vm_avl_right;
+				nodeleftright->vm_avl_left = nodeleft;
+				nodeleftright->vm_avl_right = node;
+				nodeleft->vm_avl_height = node->vm_avl_height = heightleftright;
+				nodeleftright->vm_avl_height = heightleft;
+				*nodeplace = nodeleftright;
+			}
+		}
+		else if (heightleft + 1 < heightright) {
+			/* similar to the above, just interchange 'left' <--> 'right' */
+			struct vm_area_struct * noderightright = noderight->vm_avl_right;
+			struct vm_area_struct * noderightleft = noderight->vm_avl_left;
+			int heightrightleft = heightof(noderightleft);
+			if (heightof(noderightright) >= heightrightleft) {
+				node->vm_avl_right = noderightleft; noderight->vm_avl_left = node;
+				noderight->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightrightleft);
+				*nodeplace = noderight;
+			} else {
+				noderight->vm_avl_left = noderightleft->vm_avl_right;
+				node->vm_avl_right = noderightleft->vm_avl_left;
+				noderightleft->vm_avl_right = noderight;
+				noderightleft->vm_avl_left = node;
+				noderight->vm_avl_height = node->vm_avl_height = heightrightleft;
+				noderightleft->vm_avl_height = heightright;
+				*nodeplace = noderightleft;
+			}
+		}
+		else {
+			int height = (heightleft<heightright ? heightright : heightleft) + 1;
+			if (height == node->vm_avl_height)
+				break;
+			node->vm_avl_height = height;
+		}
+	}
+}
+
+/* Insert a node into a tree. */
+static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree)
+{
+	vm_avl_key_t key = new_node->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == avl_empty)
+			break;
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key < node->vm_avl_key)
+			nodeplace = &node->vm_avl_left;
+		else
+			nodeplace = &node->vm_avl_right;
+	}
+	new_node->vm_avl_left = avl_empty;
+	new_node->vm_avl_right = avl_empty;
+	new_node->vm_avl_height = 1;
+	*nodeplace = new_node;
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+/* Insert a node into a tree, and
+ * return the node to the left of it and the node to the right of it.
+ */
+static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree,
+	struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+{
+	vm_avl_key_t key = new_node->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	*to_the_left = *to_the_right = NULL;
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == avl_empty)
+			break;
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key < node->vm_avl_key) {
+			*to_the_right = node;
+			nodeplace = &node->vm_avl_left;
+		} else {
+			*to_the_left = node;
+			nodeplace = &node->vm_avl_right;
+		}
+	}
+	new_node->vm_avl_left = avl_empty;
+	new_node->vm_avl_right = avl_empty;
+	new_node->vm_avl_height = 1;
+	*nodeplace = new_node;
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+/* Removes a node out of a tree. */
+static void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree)
+{
+	vm_avl_key_t key = node_to_delete->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	struct vm_area_struct ** nodeplace_to_delete;
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == avl_empty) {
+			/* what? node_to_delete not found in tree? */
+			printk("avl_remove: node to delete not found in tree\n");
+			return;
+		}
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key == node->vm_avl_key)
+			break;
+		if (key < node->vm_avl_key)
+			nodeplace = &node->vm_avl_left;
+		else
+			nodeplace = &node->vm_avl_right;
+	}
+	nodeplace_to_delete = nodeplace;
+	/* Have to remove node_to_delete = *nodeplace_to_delete. */
+	if (node_to_delete->vm_avl_left == avl_empty) {
+		*nodeplace_to_delete = node_to_delete->vm_avl_right;
+		stack_ptr--; stack_count--;
+	} else {
+		struct vm_area_struct *** stack_ptr_to_delete = stack_ptr;
+		struct vm_area_struct ** nodeplace = &node_to_delete->vm_avl_left;
+		struct vm_area_struct * node;
+		for (;;) {
+			node = *nodeplace;
+			if (node->vm_avl_right == avl_empty)
+				break;
+			*stack_ptr++ = nodeplace; stack_count++;
+			nodeplace = &node->vm_avl_right;
+		}
+		*nodeplace = node->vm_avl_left;
+		/* node replaces node_to_delete */
+		node->vm_avl_left = node_to_delete->vm_avl_left;
+		node->vm_avl_right = node_to_delete->vm_avl_right;
+		node->vm_avl_height = node_to_delete->vm_avl_height;
+		*nodeplace_to_delete = node; /* replace node_to_delete */
+		*stack_ptr_to_delete = &node->vm_avl_left; /* replace &node_to_delete->vm_avl_left */
+	}
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+#ifdef DEBUG_AVL
+
+/* print a list */
+static void printk_list (struct vm_area_struct * vma)
+{
+	printk("[");
+	while (vma) {
+		printk("%08lX-%08lX", vma->vm_start, vma->vm_end);
+		vma = vma->vm_next;
+		if (!vma)
+			break;
+		printk(" ");
+	}
+	printk("]");
+}
+
+/* print a tree */
+static void printk_avl (struct vm_area_struct * tree)
+{
+	if (tree != avl_empty) {
+		printk("(");
+		if (tree->vm_avl_left != avl_empty) {
+			printk_avl(tree->vm_avl_left);
+			printk("<");
+		}
+		printk("%08lX-%08lX", tree->vm_start, tree->vm_end);
+		if (tree->vm_avl_right != avl_empty) {
+			printk(">");
+			printk_avl(tree->vm_avl_right);
+		}
+		printk(")");
+	}
+}
+
+static char *avl_check_point = "somewhere";
+
+/* check a tree's consistency and balancing */
+static void avl_checkheights (struct vm_area_struct * tree)
+{
+	int h, hl, hr;
+
+	if (tree == avl_empty)
+		return;
+	avl_checkheights(tree->vm_avl_left);
+	avl_checkheights(tree->vm_avl_right);
+	h = tree->vm_avl_height;
+	hl = heightof(tree->vm_avl_left);
+	hr = heightof(tree->vm_avl_right);
+	if ((h == hl+1) && (hr <= hl) && (hl <= hr+1))
+		return;
+	if ((h == hr+1) && (hl <= hr) && (hr <= hl+1))
+		return;
+	printk("%s: avl_checkheights: heights inconsistent\n",avl_check_point);
+}
+
+/* check that all values stored in a tree are < key */
+static void avl_checkleft (struct vm_area_struct * tree, vm_avl_key_t key)
+{
+	if (tree == avl_empty)
+		return;
+	avl_checkleft(tree->vm_avl_left,key);
+	avl_checkleft(tree->vm_avl_right,key);
+	if (tree->vm_avl_key < key)
+		return;
+	printk("%s: avl_checkleft: left key %lu >= top key %lu\n",avl_check_point,tree->vm_avl_key,key);
+}
+
+/* check that all values stored in a tree are > key */
+static void avl_checkright (struct vm_area_struct * tree, vm_avl_key_t key)
+{
+	if (tree == avl_empty)
+		return;
+	avl_checkright(tree->vm_avl_left,key);
+	avl_checkright(tree->vm_avl_right,key);
+	if (tree->vm_avl_key > key)
+		return;
+	printk("%s: avl_checkright: right key %lu <= top key %lu\n",avl_check_point,tree->vm_avl_key,key);
+}
+
+/* check that all values are properly increasing */
+static void avl_checkorder (struct vm_area_struct * tree)
+{
+	if (tree == avl_empty)
+		return;
+	avl_checkorder(tree->vm_avl_left);
+	avl_checkorder(tree->vm_avl_right);
+	avl_checkleft(tree->vm_avl_left,tree->vm_avl_key);
+	avl_checkright(tree->vm_avl_right,tree->vm_avl_key);
+}
+
+/* all checks */
+static void avl_check (struct task_struct * task, char *caller)
+{
+	avl_check_point = caller;
+/*	printk("task \"%s\", %s\n",task->comm,caller); */
+/*	printk("task \"%s\" list: ",task->comm); printk_list(task->mm->mmap); printk("\n"); */
+/*	printk("task \"%s\" tree: ",task->comm); printk_avl(task->mm->mmap_avl); printk("\n"); */
+	avl_checkheights(task->mm->mmap_avl);
+	avl_checkorder(task->mm->mmap_avl);
+}
+
+#endif
+
+
 /*
  * Normal function to fix up a mapping
  * This function is the default for when an area has no specific
@@ -236,22 +658,22 @@
 	if (addr == area->vm_start && end == area->vm_end) {
 		if (area->vm_ops && area->vm_ops->close)
 			area->vm_ops->close(area);
+		remove_shared_vm_struct(area);
 		if (area->vm_inode)
 			iput(area->vm_inode);
 		return;
 	}
 
 	/* Work out to one of the ends */
-	if (addr >= area->vm_start && end == area->vm_end)
+	if (end == area->vm_end)
 		area->vm_end = addr;
-	if (addr == area->vm_start && end <= area->vm_end) {
+	else
+	if (addr == area->vm_start) {
 		area->vm_offset += (end - area->vm_start);
 		area->vm_start = end;
 	}
-
-	/* Unmapping a hole */
-	if (addr > area->vm_start && end < area->vm_end)
-	{
+	else {
+	/* Unmapping a hole: area->vm_start < addr <= end < area->vm_end */
 		/* Add end mapping -- leave beginning for below */
 		mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
 
@@ -278,6 +700,7 @@
 	if (area->vm_ops && area->vm_ops->close) {
 		area->vm_end = area->vm_start;
 		area->vm_ops->close(area);
+		remove_shared_vm_struct(area);
 	}
 	insert_vm_struct(current, mpnt);
 }
@@ -295,7 +718,7 @@
  */
 int do_munmap(unsigned long addr, size_t len)
 {
-	struct vm_area_struct *mpnt, **npp, *free;
+	struct vm_area_struct *mpnt, *prev, *next, **npp, *free;
 
 	if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
 		return -EINVAL;
@@ -309,21 +732,20 @@
 	 * every area affected in some way (by any overlap) is put
 	 * on the list.  If nothing is put on, nothing is affected.
 	 */
-	npp = &current->mm->mmap;
-	free = NULL;
-	for (mpnt = *npp; mpnt != NULL; mpnt = *npp) {
-		unsigned long end = addr+len;
-
-		if ((addr < mpnt->vm_start && end <= mpnt->vm_start) ||
-		    (addr >= mpnt->vm_end && end > mpnt->vm_end))
-		{
-			npp = &mpnt->vm_next;
-			continue;
-		}
+	mpnt = find_vma(current, addr);
+	if (!mpnt)
+		return 0;
+	avl_neighbours(mpnt, current->mm->mmap_avl, &prev, &next);
+	/* we have  prev->vm_next == mpnt && mpnt->vm_next = next */
+	/* and  addr < mpnt->vm_end  */
 
+	npp = (prev ? &prev->vm_next : &current->mm->mmap);
+	free = NULL;
+	for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) {
 		*npp = mpnt->vm_next;
 		mpnt->vm_next = free;
 		free = mpnt;
+		avl_remove(mpnt, &current->mm->mmap_avl);
 	}
 
 	if (free == NULL)
@@ -341,8 +763,6 @@
 		mpnt = free;
 		free = free->vm_next;
 
-		remove_shared_vm_struct(mpnt);
-
 		st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
 		end = addr+len;
 		end = end > mpnt->vm_end ? mpnt->vm_end : end;
@@ -358,15 +778,48 @@
 	return 0;
 }
 
+/* Build the AVL tree corresponding to the VMA list. */
+void build_mmap_avl(struct task_struct * task)
+{
+	struct vm_area_struct * vma;
+
+	task->mm->mmap_avl = NULL;
+	for (vma = task->mm->mmap; vma; vma = vma->vm_next)
+		avl_insert(vma, &task->mm->mmap_avl);
+}
+
+/* Release all mmaps. */
+void exit_mmap(struct task_struct * task)
+{
+	struct vm_area_struct * mpnt;
+
+	mpnt = task->mm->mmap;
+	task->mm->mmap = NULL;
+	task->mm->mmap_avl = NULL;
+	while (mpnt) {
+		struct vm_area_struct * next = mpnt->vm_next;
+		if (mpnt->vm_ops && mpnt->vm_ops->close)
+			mpnt->vm_ops->close(mpnt);
+		remove_shared_vm_struct(mpnt);
+		if (mpnt->vm_inode)
+			iput(mpnt->vm_inode);
+		kfree(mpnt);
+		mpnt = next;
+	}
+}
+
 /*
  * Insert vm structure into process list sorted by address
  * and into the inode's i_mmap ring.
  */
 void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp)
 {
-	struct vm_area_struct **p, *mpnt, *share;
+	struct vm_area_struct *share;
 	struct inode * inode;
 
+#if 0 /* equivalent, but slow */
+	struct vm_area_struct **p, *mpnt;
+
 	p = &t->mm->mmap;
 	while ((mpnt = *p) != NULL) {
 		if (mpnt->vm_start > vmp->vm_start)
@@ -377,6 +830,18 @@
 	}
 	vmp->vm_next = mpnt;
 	*p = vmp;
+#else
+	struct vm_area_struct * prev, * next;
+
+	avl_insert_neighbours(vmp, &t->mm->mmap_avl, &prev, &next);
+	if ((prev ? prev->vm_next : t->mm->mmap) != next)
+		printk("insert_vm_struct: tree inconsistent with list\n");
+	if (prev)
+		prev->vm_next = vmp;
+	else
+		t->mm->mmap = vmp;
+	vmp->vm_next = next;
+#endif
 
 	inode = vmp->vm_inode;
 	if (!inode)
@@ -417,21 +882,35 @@
 }
 
 /*
- * Merge a list of memory segments if possible.
+ * Merge the list of memory segments if possible.
  * Redundant vm_area_structs are freed.
  * This assumes that the list is ordered by address.
+ * We don't need to traverse the entire list, only those segments
+ * which intersect or are adjacent to a given interval.
  */
-void merge_segments(struct vm_area_struct *mpnt)
+void merge_segments (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
 {
-	struct vm_area_struct *prev, *next;
+	struct vm_area_struct *prev, *mpnt, *next;
 
-	if (mpnt == NULL)
+	mpnt = find_vma(task, start_addr);
+	if (!mpnt)
 		return;
-	
-	for(prev = mpnt, mpnt = mpnt->vm_next;
-	    mpnt != NULL;
-	    prev = mpnt, mpnt = next)
-	{
+	avl_neighbours(mpnt, task->mm->mmap_avl, &prev, &next);
+	/* we have  prev->vm_next == mpnt && mpnt->vm_next = next */
+
+	if (!prev) {
+		prev = mpnt;
+		mpnt = next;
+	}
+
+	/* prev and mpnt cycle through the list, as long as
+	 * start_addr < mpnt->vm_end && prev->vm_start < end_addr
+	 */
+	for ( ; mpnt && prev->vm_start < end_addr ; prev = mpnt, mpnt = next) {
+#if 0
+		printk("looping in merge_segments, mpnt=0x%lX\n", (unsigned long) mpnt);
+#endif
+
 		next = mpnt->vm_next;
 
 		/*
@@ -461,6 +940,7 @@
 		 * big segment can possibly merge with the next one.
 		 * The old unused mpnt is freed.
 		 */
+		avl_remove(mpnt, &task->mm->mmap_avl);
 		prev->vm_end = mpnt->vm_end;
 		prev->vm_next = mpnt->vm_next;
 		if (mpnt->vm_ops && mpnt->vm_ops->close) {
diff -u --recursive --new-file v1.1.82/linux/mm/mprotect.c linux/mm/mprotect.c
--- v1.1.82/linux/mm/mprotect.c	Mon Jan 16 14:18:33 1995
+++ linux/mm/mprotect.c	Wed Jan 18 11:00:23 1995
@@ -172,7 +172,7 @@
 
 asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot)
 {
-	unsigned long end, tmp;
+	unsigned long nstart, end, tmp;
 	struct vm_area_struct * vma, * next;
 	int error;
 
@@ -186,19 +186,14 @@
 		return -EINVAL;
 	if (end == start)
 		return 0;
-	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
-		if (!vma)
-			return -EFAULT;
-		if (vma->vm_end > start)
-			break;
-	}
-	if (vma->vm_start > start)
+	vma = find_vma(current, start);
+	if (!vma || vma->vm_start > start)
 		return -EFAULT;
 
-	for ( ; ; ) {
+	for (nstart = start ; ; ) {
 		unsigned int newflags;
 
-		/* Here we know that  vma->vm_start <= start < vma->vm_end. */
+		/* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
 
 		newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC));
 		if ((newflags & ~(newflags >> 4)) & 0xf) {
@@ -207,22 +202,22 @@
 		}
 
 		if (vma->vm_end >= end) {
-			error = mprotect_fixup(vma, start, end, newflags);
+			error = mprotect_fixup(vma, nstart, end, newflags);
 			break;
 		}
 
 		tmp = vma->vm_end;
 		next = vma->vm_next;
-		error = mprotect_fixup(vma, start, tmp, newflags);
+		error = mprotect_fixup(vma, nstart, tmp, newflags);
 		if (error)
 			break;
-		start = tmp;
+		nstart = tmp;
 		vma = next;
-		if (!vma || vma->vm_start != start) {
+		if (!vma || vma->vm_start != nstart) {
 			error = -EFAULT;
 			break;
 		}
 	}
-	merge_segments(current->mm->mmap);
+	merge_segments(current, start, end);
 	return error;
 }
diff -u --recursive --new-file v1.1.82/linux/mm/swap.c linux/mm/swap.c
--- v1.1.82/linux/mm/swap.c	Mon Jan 16 14:18:33 1995
+++ linux/mm/swap.c	Wed Jan 18 09:31:40 1995
@@ -381,14 +381,9 @@
 	/*
 	 * Find the proper vm-area
 	 */
-	vma = p->mm->mmap;
-	for (;;) {
-		if (!vma)
-			return 0;
-		if (address <= vma->vm_end)
-			break;
-		vma = vma->vm_next;
-	}
+	vma = find_vma(p, address);
+	if (!vma)
+		return 0;
 	if (address < vma->vm_start)
 		address = vma->vm_start;
 
diff -u --recursive --new-file v1.1.82/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.82/linux/net/inet/tcp.c	Wed Jan 11 21:14:30 1995
+++ linux/net/inet/tcp.c	Wed Jan 18 20:09:54 1995
@@ -121,6 +121,16 @@
  *		Linus Torvalds	:	Fixed BSD port reuse to work first syn
  *		Alan Cox	:	Reimplemented timers as per the RFC and using multiple
  *					timers for sanity. 
+ *		Alan Cox	:	Small bug fixes, and a lot of new
+ *					comments.
+ *		Alan Cox	:	Fixed dual reader crash by locking
+ *					the buffers (much like datagram.c)
+ *		Alan Cox	:	Fixed stuck sockets in probe. A probe
+ *					now gets fed up of retrying without
+ *					(even a no space) answer.
+ *		Alan Cox	:	Extracted closing code better
+ *		Alan Cox	:	Fixed the closing state machine to
+ *					resemble the RFC.
  *
  *
  * To Fix:
@@ -137,9 +147,18 @@
  *		could do with it working on IPv4
  *		User settable/learned rtt/max window/mtu
  *		Cope with MTU/device switches when retransmitting in tcp.
+ *		Fix the window handling to use PR's new code.
  *
- *
- *
+ *		Change the fundamental structure to a single send queue maintained
+ *		by TCP (removing the bogus ip stuff [thus fixing mtu drops on
+ *		active routes too]). Cut the queue off in tcp_retransmit/
+ *		tcp_transmit.
+ *		Change the receive queue to assemble as it goes. This lets us
+ *		dispose of most of tcp_sequence, half of tcp_ack and chunks of
+ *		tcp_data/tcp_read as well as the window shrink crud.
+ *		Seperate out duplicated code - tcp_alloc_skb, tcp_build_ack
+ *		tcp_queue_skb seem obvious routines to extract.
+ *	
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
  *		as published by the Free Software Foundation; either version
@@ -179,6 +198,7 @@
  *		
  *	TCP_CLOSE		socket is finished
  */
+
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -206,8 +226,10 @@
 #include <asm/segment.h>
 #include <linux/mm.h>
 
-#undef TCP_FASTPATH
-
+/*
+ *	The MSL timer is the 'normal' timer.
+ */
+ 
 #define reset_msl_timer(x,y,z)	reset_timer(x,y,z)
 
 #define SEQ_TICK 3
@@ -216,11 +238,10 @@
 
 static void tcp_close(struct sock *sk, int timeout);
 
-#ifdef TCP_FASTPATH
-unsigned long tcp_rx_miss=0, tcp_rx_hit1=0, tcp_rx_hit2=0;
-#endif
 
-/* The less said about this the better, but it works and will do for 1.2 */
+/*
+ *	The less said about this the better, but it works and will do for 1.2 
+ */
 
 static struct wait_queue *master_select_wakeup;
 
@@ -261,20 +282,20 @@
 		tcp_statistics.TcpCurrEstab++;
 }
 
-/* This routine picks a TCP windows for a socket based on
-   the following constraints
-   
-   1. The window can never be shrunk once it is offered (RFC 793)
-   2. We limit memory per socket
-   
-   For now we use NET2E3's heuristic of offering half the memory
-   we have handy. All is not as bad as this seems however because
-   of two things. Firstly we will bin packets even within the window
-   in order to get the data we are waiting for into the memory limit.
-   Secondly we bin common duplicate forms at receive time
-   
-   Better heuristics welcome
-*/
+/*
+ *	This routine picks a TCP windows for a socket based on
+ *	the following constraints
+ *  
+ *	1. The window can never be shrunk once it is offered (RFC 793)
+ *	2. We limit memory per socket
+ *   
+ *	For now we use NET2E3's heuristic of offering half the memory
+ *	we have handy. All is not as bad as this seems however because
+ *	of two things. Firstly we will bin packets even within the window
+ *	in order to get the data we are waiting for into the memory limit.
+ *	Secondly we bin common duplicate forms at receive time
+ *      Better heuristics welcome
+ */
    
 int tcp_select_window(struct sock *sk)
 {
@@ -282,15 +303,19 @@
 	
 	if(sk->window_clamp)
 		new_window=min(sk->window_clamp,new_window);
-/*
- * two things are going on here.  First, we don't ever offer a
- * window less than min(sk->mss, MAX_WINDOW/2).  This is the
- * receiver side of SWS as specified in RFC1122.
- * Second, we always give them at least the window they
- * had before, in order to avoid retracting window.  This
- * is technically allowed, but RFC1122 advises against it and
- * in practice it causes trouble.
- */
+	/*
+	 * 	Two things are going on here.  First, we don't ever offer a
+	 * 	window less than min(sk->mss, MAX_WINDOW/2).  This is the
+	 * 	receiver side of SWS as specified in RFC1122.
+	 * 	Second, we always give them at least the window they
+	 * 	had before, in order to avoid retracting window.  This
+	 * 	is technically allowed, but RFC1122 advises against it and
+	 * 	in practice it causes trouble.
+	 *
+	 * 	Fixme: This doesn't correctly handle the case where
+	 *	new_window > sk->window but not by enough to allow for the
+	 *	shift in sequence space. 
+	 */
 	if (new_window < min(sk->mss, MAX_WINDOW/2) || new_window < sk->window)
 		return(sk->window);
 	return(new_window);
@@ -316,6 +341,23 @@
 	return NULL;
 }
 
+/*
+ *	Remove a completed connection and return it. This is used by
+ *	tcp_accept() to get connections from the queue.
+ */
+
+static struct sk_buff *tcp_dequeue_established(struct sock *s)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	save_flags(flags);
+	cli(); 
+	skb=tcp_find_established(s);
+	if(skb!=NULL)
+		skb_unlink(skb);	/* Take it off the queue */
+	restore_flags(flags);
+	return skb;
+}
 
 /* 
  *	This routine closes sockets which have been at least partially
@@ -334,20 +376,6 @@
 	return;
 }
 
-static struct sk_buff *tcp_dequeue_established(struct sock *s)
-{
-	struct sk_buff *skb;
-	unsigned long flags;
-	save_flags(flags);
-	cli(); 
-	skb=tcp_find_established(s);
-	if(skb!=NULL)
-		skb_unlink(skb);	/* Take it off the queue */
-	restore_flags(flags);
-	return skb;
-}
-
-
 /*
  *	Enter the time wait state. 
  */
@@ -394,10 +422,17 @@
 		 * changing the packet, we have to issue a new IP identifier.
 		 */
 
-
 		iph = (struct iphdr *)(skb->data + dev->hard_header_len);
 		th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2));
 		size = skb->len - (((unsigned char *) th) - skb->data);
+		
+		/*
+		 *	Note: We ought to check for window limits here but
+		 *	currently this is done (less efficiently) elsewhere.
+		 *	We do need to check for a route change but can't handle
+		 *	that until we have the new 1.3.x buffers in.
+		 *
+		 */
 
 		iph->id = htons(ip_id_count++);
 		ip_send_check(iph);
@@ -444,18 +479,21 @@
 		/*
 		 *	Count retransmissions
 		 */
+		 
 		sk->retransmits++;
 		sk->prot->retransmits ++;
 
 		/*
 		 *	Only one retransmit requested.
 		 */
+	
 		if (!all)
 			break;
 
 		/*
 		 *	This should cut it off before we send too many packets.
 		 */
+
 		if (sk->retransmits >= sk->cong_window)
 			break;
 		skb = skb->link3;
@@ -502,6 +540,10 @@
 	 * defined in the protocol as the maximum possible RTT.  I guess
 	 * we'll have to use something other than TCP to talk to the
 	 * University of Mars.
+	 *
+	 * PAWS allows us longer timeouts and large windows, so once
+	 * implemented ftp to mars will work nicely. We will have to fix
+	 * the 120 second clamps though!
 	 */
 
 	sk->retransmits++;
@@ -537,10 +579,59 @@
 }
 
 /*
- *	The TCP retransmit timer.
+ *	A write timeout has occured. Process the after effects.
  */
 
+static int tcp_write_timeout(struct sock *sk)
+{
+	/*
+	 *	Look for a 'soft' timeout.
+	 */
+	if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
+		|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) 
+	{
+		/*
+		 *	Attempt to recover if arp has changed (unlikely!) or
+		 *	a route has shifted (not supported prior to 1.3).
+		 */
+		arp_destroy (sk->daddr, 0);
+		ip_route_check (sk->daddr);
+	}
+	/*
+	 *	Has it gone just too far ?
+	 */
+	if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) 
+	{
+		sk->err = ETIMEDOUT;
+		/*
+		 *	Time wait the socket 
+		 */
+		if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING) 
+		{
+			tcp_set_state(sk,TCP_TIME_WAIT);
+			reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+		}
+		else
+		{
+			/*
+			 *	Clean up time.
+			 */
+			sk->prot->close (sk, 1);
+			return 0;
+		}
+	}
+	return 1;
+}
 
+/*
+ *	The TCP retransmit timer. This lacks a few small details.
+ *
+ *	1. 	An initial rtt timeout on the probe0 should cause what we can
+ *		of the first write queue buffer to be split and sent.
+ *	2.	On a 'major timeout' as defined by RFC1122 we shouldn't report
+ *		ETIMEDOUT if we know an additional 'soft' error caused this.
+ *		tcp_err should save a 'soft error' for us.
+ */
 
 static void retransmit_timer(unsigned long data)
 {
@@ -554,7 +645,8 @@
 	cli();
 	if (sk->inuse || in_bh) 
 	{
-		sk->retransmit_timer.expires = 10;
+		/* Try again in 1 second */
+		sk->retransmit_timer.expires = HZ;
 		add_timer(&sk->retransmit_timer);
 		sti();
 		return;
@@ -579,7 +671,8 @@
 		/* Window probing */
 		case TIME_PROBE0:
 			tcp_send_probe0(sk);
-			release_sock (sk);
+			if(tcp_write_timeout(sk))
+				release_sock (sk);
 			break;
 		/* Retransmitting */
 		case TIME_WRITE:
@@ -599,6 +692,10 @@
 			} 
 			else 
 			{
+				/*
+				 *	Kicked by a delayed ack. Reset timer
+				 *	correctly now
+				 */
 				if (jiffies < skb->when + sk->rto) 
 				{
 					reset_xmit_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies);
@@ -607,29 +704,12 @@
 					break;
 				}
 				restore_flags(flags);
-				/* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq,
-					sk->retransmits, sk->packets_out, sk->cong_window); */
+				/*
+				 *	Retransmission
+				 */
 				sk->prot->retransmit (sk, 0);
-				if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
-					|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) 
-				{
-					arp_destroy (sk->daddr, 0);
-					ip_route_check (sk->daddr);
-				}
-				if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) 
-				{
-					sk->err = ETIMEDOUT;
-					if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING) 
-					{
-						sk->state = TCP_TIME_WAIT;
-						reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-					}
-					else
-					{
-						sk->prot->close (sk, 1);
-							break;
-					}
-				}
+				if(!tcp_write_timeout(sk))
+					break;
 			}
 			release_sock (sk);
 			break;
@@ -646,37 +726,8 @@
 			if (sk->prot->write_wakeup)
 				  sk->prot->write_wakeup (sk);
 			sk->retransmits++;
-			if (sk->shutdown == SHUTDOWN_MASK) 
-			{
-				sk->prot->close (sk, 1);
-				sk->state = TCP_CLOSE;
-			}
-			if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
-				|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) 
-			{
-				arp_destroy (sk->daddr, 0);
-				ip_route_check (sk->daddr);
+			if(tcp_write_timeout(sk))
 				release_sock (sk);
-				break;
-			}
-			if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) 
-			{
-				arp_destroy (sk->daddr, 0);
-				sk->err = ETIMEDOUT;
-				if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) 
-				{
-					sk->state = TCP_TIME_WAIT;
-					if (!sk->dead)
-						sk->state_change(sk);
-					release_sock (sk);
-				  } 
-				  else 
-				  {
-					sk->prot->close (sk, 1);
-				  }
-				  break;
-			}
-			release_sock (sk);
 			break;
 		default:
 			printk ("rexmit_timer: timer expired - reason unknown\n");
@@ -752,7 +803,8 @@
 
 /*
  *	Walk down the receive queue counting readable data until we hit the end or we find a gap
- *	in the received data queue (ie a frame missing that needs sending to us)
+ *	in the received data queue (ie a frame missing that needs sending to us). Not
+ *	sorting using two queues as data arrives makes life so much harder.
  */
 
 static int tcp_readable(struct sock *sk)
@@ -779,7 +831,10 @@
 	counted = sk->copied_seq;	/* Where we are at the moment */
 	amount = 0;
   
-	/* Do until a push or until we are out of data. */
+	/* 
+	 *	Do until a push or until we are out of data. 
+	 */
+	 
 	do 
 	{
 		if (before(counted, skb->h.th->seq)) 	/* Found a hole so stops here */
@@ -823,68 +878,77 @@
 	return(amount);
 }
 
-
 /*
- *	Wait for a TCP event. Note the oddity with SEL_IN and reading. The
- *	listening socket has a receive queue of sockets to accept.
+ * LISTEN is a special case for select..
  */
+static int tcp_listen_select(struct sock *sk, int sel_type, select_table *wait)
+{
+	if (sel_type == SEL_IN) {
+		int retval;
+
+		sk->inuse = 1;
+		retval = (tcp_find_established(sk) != NULL);
+		release_sock(sk);
+		if (!retval)
+			select_wait(&master_select_wakeup,wait);
+		return retval;
+	}
+	return 0;
+}
+
 
-static int do_tcp_select(struct sock *sk, int sel_type, select_table *wait)
+/*
+ *	Wait for a TCP event.
+ *
+ *	Note that we don't need to set "sk->inuse", as the upper select layers
+ *	take care of normal races (between the test and the event) and we don't
+ *	go look at any of the socket buffers directly.
+ */
+static int tcp_select(struct sock *sk, int sel_type, select_table *wait)
 {
-	switch(sel_type) 
-	{
-		case SEL_IN:
-			if (sk->err)
-				return 1;
-			if (sk->state == TCP_LISTEN) {
-				select_wait(&master_select_wakeup,wait);
-				return (tcp_find_established(sk) != NULL);
-			}
-			if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
-				return 0;
-			if (sk->acked_seq != sk->copied_seq)
-				return 1;
-			if (sk->shutdown & RCV_SHUTDOWN)
-				return 1;
-			return 0;
+	if (sk->state == TCP_LISTEN)
+		return tcp_listen_select(sk, sel_type, wait);
 
-		case SEL_OUT:
-			if (sk->shutdown & SEND_SHUTDOWN) {
-				/* FIXME: should this return an error? */
-				return 0;
-			}
+	switch(sel_type) {
+	case SEL_IN:
+		if (sk->err)
+			return 1;
+		if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
+			break;
 
-			/*
-			 * This is now right thanks to a small fix
-			 * by Matt Dillon.
-			 */
+		if (sk->shutdown & RCV_SHUTDOWN)
+			return 1;
 			
-			if (sk->prot->wspace(sk) >= sk->mtu+128+sk->prot->max_header) 
-			{
-				/* This should cause connect to work ok. */
-				if (sk->state == TCP_SYN_RECV ||
-				    sk->state == TCP_SYN_SENT) return 0;
-				return 1;
-			}
-			return 0;
+		if (sk->acked_seq == sk->copied_seq)
+			break;
 
-		case SEL_EX:
-			if (sk->err || sk->urg_data)
-				return 1;
+		if (sk->urg_seq != sk->copied_seq ||
+		    sk->acked_seq != sk->copied_seq+1 ||
+		    sk->urginline || !sk->urg_data)
+			return 1;
+		break;
+
+	case SEL_OUT:
+		if (sk->shutdown & SEND_SHUTDOWN) 
 			return 0;
- 	}
- 	return 0;
-}
+		if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
+			break;
+		/*
+		 * This is now right thanks to a small fix
+		 * by Matt Dillon.
+		 */
 
-static int tcp_select(struct sock *sk, int sel_type, select_table *wait)
-{
-	int retval;
+		if (sk->prot->wspace(sk) < sk->mtu+128+sk->prot->max_header)
+			break;
+		return 1;
 
-	sk->inuse = 1;
+	case SEL_EX:
+		if (sk->err || sk->urg_data)
+			return 1;
+		break;
+	}
 	select_wait(sk->sleep, wait);
-	retval = do_tcp_select(sk, sel_type, wait);
-	release_sock(sk);
-	return retval;
+	return 0;
 }
 
 int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
@@ -1037,15 +1101,26 @@
 	return;
 }
 
+/*
+ *	This is the main buffer sending routine. We queue the buffer
+ *	having checked it is sane seeming.
+ */
+ 
 static void tcp_send_skb(struct sock *sk, struct sk_buff *skb)
 {
 	int size;
 	struct tcphdr * th = skb->h.th;
 
-	/* length of packet (not counting length of pre-tcp headers) */
+	/*
+	 *	length of packet (not counting length of pre-tcp headers) 
+	 */
+	 
 	size = skb->len - ((unsigned char *) th - skb->data);
 
-	/* sanity check it.. */
+	/*
+	 *	Sanity check it.. 
+	 */
+	 
 	if (size < sizeof(struct tcphdr) || size > skb->len) 
 	{
 		printk("tcp_send_skb: bad skb (skb = %p, data = %p, th = %p, len = %lu)\n",
@@ -1054,7 +1129,11 @@
 		return;
 	}
 
-	/* If we have queued a header size packet.. */
+	/*
+	 *	If we have queued a header size packet.. (these crash a few
+	 *	tcp stacks if ack is not set)
+	 */
+	 
 	if (size == sizeof(struct tcphdr)) 
 	{
 		/* If its got a syn or fin its notionally included in the size..*/
@@ -1066,9 +1145,21 @@
 		}
 	}
 
+	/*
+	 *	Actual processing.
+	 */
+	 
 	tcp_statistics.TcpOutSegs++;  
-
 	skb->h.seq = ntohl(th->seq) + size - 4*th->doff;
+	
+	/*
+	 *	We must queue if
+	 *
+	 *	a) The right edge of this frame exceeds the window
+	 *	b) We are retransmitting (Nagle's rule)
+	 *	c) We have too many packets 'in flight'
+	 */
+	 
 	if (after(skb->h.seq, sk->window_seq) ||
 	    (sk->retransmits && sk->ip_xmit_timeout == TIME_WRITE) ||
 	     sk->packets_out >= sk->cong_window) 
@@ -1082,24 +1173,58 @@
 			skb_unlink(skb);
 		}
 		skb_queue_tail(&sk->write_queue, skb);
+		
+		/*
+		 *	If we don't fit we have to start the zero window
+		 *	probes. This is broken - we really need to do a partial
+		 *	send _first_ (This is what causes the Cisco and PC/TCP
+		 *	grief).
+		 */
+		 
 		if (before(sk->window_seq, sk->write_queue.next->h.seq) &&
-		    sk->send_head == NULL &&
-		    sk->ack_backlog == 0)
+		    sk->send_head == NULL && sk->ack_backlog == 0)
 			reset_xmit_timer(sk, TIME_PROBE0, sk->rto);
 	} 
 	else 
 	{
+		/*
+		 *	This is going straight out
+		 */
+		 
 		th->ack_seq = ntohl(sk->acked_seq);
 		th->window = ntohs(tcp_select_window(sk));
 
 		tcp_send_check(th, sk->saddr, sk->daddr, size, sk);
 
 		sk->sent_seq = sk->write_seq;
+		
+		/*
+		 *	This is mad. The tcp retransmit queue is put together
+		 *	by the ip layer. This causes half the problems with
+		 *	unroutable FIN's and other things.
+		 */
+		 
 		sk->prot->queue_xmit(sk, skb->dev, skb, 0);
+		
+		/*
+		 *	Set for next retransmit based on expected ACK time.
+		 *	FIXME: We set this every time which means our 
+		 *	retransmits are really about a window behind.
+		 */
+
 		reset_xmit_timer(sk, TIME_WRITE, sk->rto);
 	}
 }
 
+/*
+ *	Locking problems lead us to a messy situation where we can have
+ *	multiple partially complete buffers queued up. This is really bad
+ *	as we don't want to be sending partial buffers. Fix this with
+ *	a semaphore or similar to lock tcp_write per socket.
+ *
+ *	These routines are pretty self descriptive.
+ */
+ 
 struct sk_buff * tcp_dequeue_partial(struct sock * sk)
 {
 	struct sk_buff * skb;
@@ -1116,6 +1241,10 @@
 	return skb;
 }
 
+/*
+ *	Empty the partial queue
+ */
+ 
 static void tcp_send_partial(struct sock *sk)
 {
 	struct sk_buff *skb;
@@ -1126,6 +1255,10 @@
 		tcp_send_skb(sk, skb);
 }
 
+/*
+ *	Queue a partial frame
+ */
+ 
 void tcp_enqueue_partial(struct sk_buff * skb, struct sock * sk)
 {
 	struct sk_buff * tmp;
@@ -1138,6 +1271,9 @@
 		del_timer(&sk->partial_timer);
 	sk->partial = skb;
 	init_timer(&sk->partial_timer);
+	/*
+	 *	Wait up to 1 second for the buffer to fill.
+	 */
 	sk->partial_timer.expires = HZ;
 	sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial;
 	sk->partial_timer.data = (unsigned long) sk;
@@ -1163,6 +1299,7 @@
 
 	if(sk->zapped)
 		return;		/* We have been reset, we may not send again */
+		
 	/*
 	 * We need to grab some memory, and put together an ack,
 	 * and then put it into the queue to be sent.
@@ -1171,21 +1308,34 @@
 	buff = sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
 	if (buff == NULL) 
 	{
-		/* Force it to send an ack. */
+		/* 
+		 *	Force it to send an ack. We don't have to do this
+		 *	(ACK is unreliable) but its much better use of 
+		 *	bandwidth on slow links to send a spare ack than
+		 *	resend packets. 
+		 */
+		 
 		sk->ack_backlog++;
 		if (sk->ip_xmit_timeout != TIME_WRITE && tcp_connected(sk->state)) 
 		{
-			reset_xmit_timer(sk, TIME_WRITE, 10);
+			reset_xmit_timer(sk, TIME_WRITE, HZ);
 		}
 		return;
 	}
 
+	/*
+	 *	Assemble a suitable TCP frame
+	 */
+	 
 	buff->len = sizeof(struct tcphdr);
 	buff->sk = sk;
 	buff->localroute = sk->localroute;
 	t1 =(struct tcphdr *) buff->data;
 
-	/* Put in the IP header and routing stuff. */
+	/* 
+	 *	Put in the IP header and routing stuff. 
+	 */
+	 
 	tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,
 				IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
 	if (tmp < 0) 
@@ -1197,8 +1347,7 @@
 	buff->len += tmp;
 	t1 =(struct tcphdr *)((char *)t1 +tmp);
 
-	/* FIXME: */
-	memcpy(t1, th, sizeof(*t1)); /* this should probably be removed */
+	memcpy(t1, th, sizeof(*t1));
 
 	/*
 	 *	Swap the send and the receive. 
@@ -1217,6 +1366,13 @@
 	t1->syn = 0;
 	t1->psh = 0;
 	t1->fin = 0;
+	
+	/*
+	 *	If we have nothing queued for transmit and the transmit timer
+	 *	is on we are just doing an ACK timeout and need to switch
+	 *	to a keepalive.
+	 */
+	 
 	if (ack == sk->acked_seq) 
 	{
 		sk->ack_backlog = 0;
@@ -1232,6 +1388,11 @@
 			}
 		}
   	}
+  	
+  	/*
+  	 *	Fill in the packet and send it
+  	 */
+  	 
   	t1->ack_seq = ntohl(ack);
   	t1->doff = sizeof(*t1)/4;
   	tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
@@ -1249,7 +1410,6 @@
 extern __inline int tcp_build_header(struct tcphdr *th, struct sock *sk, int push)
 {
 
-	/* FIXME: want to get rid of this. */
 	memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
 	th->seq = htonl(sk->write_seq);
 	th->psh =(push == 0) ? 1 : 0;
@@ -1297,9 +1457,9 @@
 			return(tmp);
 		}
 
-	/*
-	 *	First thing we do is make sure that we are established. 
-	 */
+		/*
+		 *	First thing we do is make sure that we are established. 
+		 */
 	
 		if (sk->shutdown & SEND_SHUTDOWN) 
 		{
@@ -1311,10 +1471,9 @@
 			return(-EPIPE);
 		}
 
-
-	/* 
-	 *	Wait for a connection to finish.
-	 */
+		/* 
+		 *	Wait for a connection to finish.
+		 */
 	
 		while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) 
 		{
@@ -1597,6 +1756,9 @@
 	return(copied);
 }
 
+/*
+ *	This is just a wrapper. 
+ */
 
 static int tcp_sendto(struct sock *sk, unsigned char *from,
 	   int len, int nonblock, unsigned flags,
@@ -1618,6 +1780,11 @@
 }
 
 
+/*
+ *	Send an ack if one is backlogged at this point. Ought to merge
+ *	this with tcp_send_ack().
+ */
+ 
 static void tcp_read_wakeup(struct sock *sk)
 {
 	int tmp;
@@ -1643,7 +1810,7 @@
 	if (buff == NULL) 
 	{
 		/* Try again real soon. */
-		reset_xmit_timer(sk, TIME_WRITE, 10);
+		reset_xmit_timer(sk, TIME_WRITE, HZ);
 		return;
  	}
 
@@ -1711,13 +1878,13 @@
 	left = sk->prot->rspace(sk);
  
 	/*
-	 * We have to loop through all the buffer headers,
-	 * and try to free up all the space we can.
+	 *	We have to loop through all the buffer headers,
+	 *	and try to free up all the space we can.
 	 */
 
 	while((skb=skb_peek(&sk->receive_queue)) != NULL) 
 	{
-		if (!skb->used) 
+		if (!skb->used || skb->users) 
 			break;
 		skb_unlink(skb);
 		skb->sk = sk;
@@ -1727,10 +1894,10 @@
 	restore_flags(flags);
 
 	/*
-	 * FIXME:
-	 * At this point we should send an ack if the difference
-	 * in the window, and the amount of space is bigger than
-	 * TCP_WINDOW_DIFF.
+	 *	FIXME:
+	 *	At this point we should send an ack if the difference
+	 *	in the window, and the amount of space is bigger than
+	 *	TCP_WINDOW_DIFF.
 	 */
 
 	if(sk->debug)
@@ -1779,14 +1946,19 @@
 
 
 /*
- *	Handle reading urgent data. 
+ *	Handle reading urgent data. BSD has very simple semantics for
+ *	this, no blocking and very strange errors 8)
  */
  
 static int tcp_read_urg(struct sock * sk, int nonblock,
 	     unsigned char *to, int len, unsigned flags)
 {
+	/*
+	 *	No URG data to read
+	 */
 	if (sk->urginline || !sk->urg_data || sk->urg_data == URG_READ)
-		return -EINVAL;
+		return -EINVAL;	/* Yes this is right ! */
+		
 	if (sk->err) 
 	{
 		int tmp = -sk->err;
@@ -1841,17 +2013,29 @@
 	struct wait_queue wait = { current, NULL };
 	int copied = 0;
 	unsigned long peek_seq;
-	unsigned long *seq;
+	volatile unsigned long *seq;	/* So gcc doesnt overoptimise */
 	unsigned long used;
 
-	/* This error should be checked. */
+	/* 
+	 *	This error should be checked. 
+	 */
+	 
 	if (sk->state == TCP_LISTEN)
 		return -ENOTCONN;
 
-	/* Urgent data needs to be handled specially. */
+	/*
+	 *	Urgent data needs to be handled specially. 
+	 */
+	 
 	if (flags & MSG_OOB)
 		return tcp_read_urg(sk, nonblock, to, len, flags);
 
+	/*
+	 *	Copying sequence to update. This is volatile to handle
+	 *	the multi-reader case neatly (memcpy_to/fromfs might be 
+	 *	inline and thus not flush cached variables otherwise).
+	 */
+	 
 	peek_seq = sk->copied_seq;
 	seq = &sk->copied_seq;
 	if (flags & MSG_PEEK)
@@ -1867,9 +2051,14 @@
 		/*
 		 * Are we at urgent data? Stop if we have read anything.
 		 */
+		 
 		if (copied && sk->urg_data && sk->urg_seq == *seq)
 			break;
 
+		/*
+		 *	Next get a buffer.
+		 */
+		 
 		current->state = TASK_INTERRUPTIBLE;
 
 		skb = skb_peek(&sk->receive_queue);
@@ -1940,11 +2129,26 @@
 		continue;
 
 	found_ok_skb:
-		/* Ok so how much can we use ? */
+		/*
+		 *	Lock the buffer. We can be fairly relaxed as
+		 *	an interrupt will never steal a buffer we are 
+		 *	using unless I've missed something serious in
+		 *	tcp_data.
+		 */
+		
+		skb->users++;
+		
+		/*
+		 *	Ok so how much can we use ? 
+		 */
+		 
 		used = skb->len - offset;
 		if (len < used)
 			used = len;
-		/* do we have urgent data here? */
+		/*
+		 *	Do we have urgent data here? 
+		 */
+		
 		if (sk->urg_data) 
 		{
 			unsigned long urg_offset = sk->urg_seq - *seq;
@@ -1963,17 +2167,43 @@
 					used = urg_offset;
 			}
 		}
-		/* Copy it */
+		
+		/*
+		 *	Copy it - We _MUST_ update *seq first so that we
+		 *	don't ever double read when we have dual readers
+		 */
+		 
+		*seq += used;
+
+		/*
+		 *	This memcpy_tofs can sleep. If it sleeps and we
+		 *	do a second read it relies on the skb->users to avoid
+		 *	a crash when cleanup_rbuf() gets called.
+		 */
+		 
 		memcpy_tofs(to,((unsigned char *)skb->h.th) +
 			skb->h.th->doff*4 + offset, used);
 		copied += used;
 		len -= used;
 		to += used;
-		*seq += used;
+		
+		/*
+		 *	We now will not sleep again until we are finished
+		 *	with skb. Sorry if you are doing the SMP port
+		 *	but you'll just have to fix it neatly ;)
+		 */
+		 
+		skb->users --;
+		
 		if (after(sk->copied_seq,sk->urg_seq))
 			sk->urg_data = 0;
 		if (used + offset < skb->len)
 			continue;
+		
+		/*
+		 *	Process the FIN.
+		 */
+
 		if (skb->h.th->fin)
 			goto found_fin_ok;
 		if (flags & MSG_PEEK)
@@ -1985,6 +2215,11 @@
 		++*seq;
 		if (flags & MSG_PEEK)
 			break;
+			
+		/*
+		 *	All is done
+		 */
+		 
 		skb->used = 1;
 		sk->shutdown |= RCV_SHUTDOWN;
 		break;
@@ -1999,67 +2234,92 @@
 	return copied;
 }
 
- 
 /*
- *	Shutdown the sending side of a connection.
+ *	State processing on a close. This implements the state shift for
+ *	sending our FIN frame. Note that we only send a FIN for some 
+ *	states. A shutdown() may have already sent the FIN, or we may be
+ *	closed.
  */
-
-void tcp_shutdown(struct sock *sk, int how)
+ 
+static int tcp_close_state(struct sock *sk)
 {
-	struct sk_buff *buff;
-	struct tcphdr *t1, *th;
-	struct proto *prot;
-	int tmp;
-	struct device *dev = NULL;
-
-	/*
-	 * We need to grab some memory, and put together a FIN,
-	 * and then put it into the queue to be sent.
-	 * FIXME:
-	 *
-	 *	Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
-	 *	Most of this is guesswork, so maybe it will work...
-	 */
-
-	if (!(how & SEND_SHUTDOWN)) 
-		return;
-	 
-	/*
-	 *	If we've already sent a FIN, return. 
-	 */
-	 
-	if (sk->state == TCP_FIN_WAIT1 ||
-	    sk->state == TCP_FIN_WAIT2 ||
-	    sk->state == TCP_CLOSING ||
-	    sk->state == TCP_LAST_ACK ||
-	    sk->state == TCP_TIME_WAIT
-	) 
+	int ns=TCP_CLOSE;
+	int send_fin=0;
+	switch(sk->state)
 	{
-		return;
-	}
-	sk->inuse = 1;
-
-	/*
-	 * flag that the sender has shutdown
-	 */
-
-	sk->shutdown |= SEND_SHUTDOWN;
-
+		case TCP_SYN_SENT:	/* No SYN back, no FIN needed */
+			break;
+		case TCP_SYN_RECV:
+		case TCP_ESTABLISHED:	/* Closedown begin */
+			ns=TCP_FIN_WAIT1;
+			send_fin=1;
+			break;
+		case TCP_FIN_WAIT1:	/* Already closing, or FIN sent: no change */
+		case TCP_FIN_WAIT2:
+		case TCP_CLOSING:
+			ns=sk->state;
+			break;
+		case TCP_CLOSE:
+		case TCP_LISTEN:
+			break;
+		case TCP_CLOSE_WAIT:	/* They have FIN'd us. We send our FIN and
+					   wait only for the ACK */
+			ns=TCP_LAST_ACK;
+			send_fin=1;
+	}
+	
+	tcp_set_state(sk,ns);
+		
 	/*
-	 *  Clear out any half completed packets. 
-	 */
+	 *	This is a (useful) BSD violating of the RFC. There is a
+	 *	problem with TCP as specified in that the other end could
+	 *	keep a socket open forever with no application left this end.
+	 *	We use a 3 minute timeout (about the same as BSD) then kill
+	 *	our end. If they send after that then tough - BUT: long enough
+	 *	that we won't make the old 4*rto = almost no time - whoops
+	 *	reset mistake.
+	 */
+	if(sk->dead && ns==TCP_FIN_WAIT2)
+	{
+		int timer_active=del_timer(&sk->timer);
+		if(timer_active)
+			add_timer(&sk->timer);
+		else
+			reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT);
+	}
+	
+	return send_fin;
+}
 
-	if (sk->partial)
-		tcp_send_partial(sk);
+/*
+ *	Send a fin.
+ */
 
-	prot =(struct proto *)sk->prot;
-	th =(struct tcphdr *)&sk->dummy_th;
+static void tcp_send_fin(struct sock *sk)
+{
+	struct proto *prot =(struct proto *)sk->prot;
+	struct tcphdr *th =(struct tcphdr *)&sk->dummy_th;
+	struct tcphdr *t1;
+	struct sk_buff *buff;
+	struct device *dev=NULL;
+	int tmp;
+		
 	release_sock(sk); /* in case the malloc sleeps. */
+	
 	buff = prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL);
+	sk->inuse = 1;
+
 	if (buff == NULL)
+	{
+		/* This is a disaster if it occurs */
+		printk("tcp_send_fin: Impossible malloc failure");
 		return;
-	sk->inuse = 1;
+	}
 
+	/*
+	 *	Administrivia
+	 */
+	 
 	buff->sk = sk;
 	buff->len = sizeof(*t1);
 	buff->localroute = sk->localroute;
@@ -2076,27 +2336,19 @@
 	{
   		/*
   		 *	Finish anyway, treat this as a send that got lost. 
-  		 *
-  		 *	Enter FIN_WAIT1 on normal shutdown, which waits for
-  		 *	written data to be completely acknowledged along
-  		 *	with an acknowledge to our FIN.
-  		 *
-  		 *	Enter FIN_WAIT2 on abnormal shutdown -- close before
-  		 *	connection established.
+  		 *	(Not good).
   		 */
+  		 
 	  	buff->free = 1;
 		prot->wfree(sk,buff->mem_addr, buff->mem_len);
-
-		if (sk->state == TCP_ESTABLISHED)
-			tcp_set_state(sk,TCP_FIN_WAIT1);
-		else if(sk->state == TCP_CLOSE_WAIT)
-			tcp_set_state(sk,TCP_LAST_ACK);
-		else
-			tcp_set_state(sk,TCP_FIN_WAIT2);
-
-		release_sock(sk);
+		sk->write_seq++;
 		return;
 	}
+	
+	/*
+	 *	We ought to check if the end of the queue is a buffer and
+	 *	if so simply add the fin to that buffer, not send it ahead.
+	 */
 
 	t1 =(struct tcphdr *)((char *)t1 +tmp);
 	buff->len += tmp;
@@ -2123,7 +2375,7 @@
   		buff->free = 0;
 		if (buff->next != NULL) 
 		{
-			printk("tcp_shutdown: next != NULL\n");
+			printk("tcp_send_fin: next != NULL\n");
 			skb_unlink(buff);
 		}
 		skb_queue_tail(&sk->write_queue, buff);
@@ -2134,14 +2386,61 @@
 		sk->prot->queue_xmit(sk, dev, buff, 0);
 		reset_xmit_timer(sk, TIME_WRITE, sk->rto);
 	}
+}
 
-	if (sk->state == TCP_ESTABLISHED) 
-		tcp_set_state(sk,TCP_FIN_WAIT1);
-	else if (sk->state == TCP_CLOSE_WAIT)
-		tcp_set_state(sk,TCP_LAST_ACK);
-	else
-		tcp_set_state(sk,TCP_FIN_WAIT2);
+/*
+ *	Shutdown the sending side of a connection. Much like close except
+ *	that we don't receive shut down or set sk->dead=1.
+ */
+
+void tcp_shutdown(struct sock *sk, int how)
+{
+	/*
+	 *	We need to grab some memory, and put together a FIN,
+	 *	and then put it into the queue to be sent.
+	 *		Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
+	 */
+
+	if (!(how & SEND_SHUTDOWN)) 
+		return;
+	 
+	/*
+	 *	If we've already sent a FIN, or its a closed state
+	 */
+	 
+	if (sk->state == TCP_FIN_WAIT1 ||
+	    sk->state == TCP_FIN_WAIT2 ||
+	    sk->state == TCP_CLOSING ||
+	    sk->state == TCP_LAST_ACK ||
+	    sk->state == TCP_TIME_WAIT || 
+	    sk->state == TCP_CLOSE ||
+	    sk->state == TCP_LISTEN
+	  )
+	{
+		return;
+	}
+	sk->inuse = 1;
 
+	/*
+	 * flag that the sender has shutdown
+	 */
+
+	sk->shutdown |= SEND_SHUTDOWN;
+
+	/*
+	 *  Clear out any half completed packets. 
+	 */
+
+	if (sk->partial)
+		tcp_send_partial(sk);
+		
+	/*
+	 *	FIN if needed
+	 */
+	 
+	if(tcp_close_state(sk))
+		tcp_send_fin(sk);
+		
 	release_sock(sk);
 }
 
@@ -2626,17 +2925,22 @@
 
 static void tcp_close(struct sock *sk, int timeout)
 {
-  	struct sk_buff *buff;
-	struct tcphdr *t1, *th;
-	struct proto *prot;
-	struct device *dev=NULL;
-	int tmp;
-
 	/*
 	 * We need to grab some memory, and put together a FIN,	
 	 * and then put it into the queue to be sent.
 	 */
+	
 	sk->inuse = 1;
+	
+	if(sk->state == TCP_LISTEN)
+	{
+		/* Special case */
+		tcp_set_state(sk, TCP_CLOSE);
+		tcp_close_pending(sk, timeout);
+		release_sock(sk);
+		return;
+	}
+	
 	sk->keepopen = 1;
 	sk->shutdown = SHUTDOWN_MASK;
 
@@ -2645,22 +2949,16 @@
 
 	if (timeout == 0) 
 	{
+		struct sk_buff *skb;
+		
 		/*
 		 *  We need to flush the recv. buffs.  We do this only on the
 		 *  descriptor close, not protocol-sourced closes, because the
 		 *  reader process may not have drained the data yet!
 		 */
-
-		if (skb_peek(&sk->receive_queue) != NULL) 
-		{
-			struct sk_buff *skb;
-			if(sk->debug)
-        			printk("Clean rcv queue\n");
-  			while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
-  				kfree_skb(skb, FREE_READ);
-			if(sk->debug)
-				printk("Cleaned.\n");
-		}
+		 
+		while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+			kfree_skb(skb, FREE_READ);
 	}
 
 	/*
@@ -2668,175 +2966,45 @@
 	 */
 	 
 	if (sk->partial) 
-	{
 		tcp_send_partial(sk);
+		
+	/*
+	 *	Timeout is not the same thing - however the code likes
+	 *	to send both the same way (sigh).
+	 */
+	 
+	if(timeout)
+	{
+		/*
+		 *	Time wait to avoid port reusage accidents if 
+		 *	appropriate. If we have timed out from one
+		 *	of these states then move straight to close.
+		 */
+		 
+		if( sk->state == TCP_TIME_WAIT || sk->state == TCP_LAST_ACK 
+			|| sk->state == TCP_SYN_SENT || sk->state == TCP_CLOSE)
+			tcp_set_state(sk, TCP_CLOSE);	/* Dead */
+		else
+			tcp_time_wait(sk);			
 	}
-
-	switch(sk->state) 
+	else
 	{
-		case TCP_FIN_WAIT1:
-		case TCP_FIN_WAIT2:
-		case TCP_CLOSING:
-			/*
-			 * These states occur when we have already closed out
-			 * our end.  If there is no timeout, we do not do
-			 * anything.  We may still be in the middle of sending
-			 * the remainder of our buffer, for example...
-			 * resetting the timer would be inappropriate.
-			 *
-			 * XXX if retransmit count reaches limit, is tcp_close()
-			 * called with timeout == 1 ? if not, we need to fix that.
-			 */
-			if (!timeout) {
-				int timer_active;
-
-				timer_active = del_timer(&sk->timer);
-				if (timer_active)
-					add_timer(&sk->timer);
-				else
-					reset_msl_timer(sk, TIME_CLOSE, 4 * sk->rto);
-			}
-			if (timeout) 
-				tcp_time_wait(sk);
-			release_sock(sk);
-			return;	/* break causes a double release - messy */
-		case TCP_TIME_WAIT:
-		case TCP_LAST_ACK:
-			/*
-			 * A timeout from these states terminates the TCB.
-			 */
-			if (timeout) 
-			{
-		  		tcp_set_state(sk,TCP_CLOSE);
-			}
-			release_sock(sk);
-			return;
-		case TCP_LISTEN:
-			/* we need to drop any sockets which have been connected,
-			   but have not yet been accepted. */
-			tcp_set_state(sk,TCP_CLOSE);
-			tcp_close_pending(sk, timeout);
-			release_sock(sk);
-			return;
-		case TCP_CLOSE:
-			release_sock(sk);
-			return;
-		case TCP_CLOSE_WAIT:
-		case TCP_ESTABLISHED:
-		case TCP_SYN_SENT:
-		case TCP_SYN_RECV:
-			prot =(struct proto *)sk->prot;
-			th =(struct tcphdr *)&sk->dummy_th;
-			buff = prot->wmalloc(sk, MAX_FIN_SIZE, 1, GFP_ATOMIC);
-			if (buff == NULL) 
-			{
-				/* This will force it to try again later. */
-				/* Or it would have if someone released the socket
-				   first. Anyway it might work now */
-				release_sock(sk);
-				if (sk->state != TCP_CLOSE_WAIT)
-					tcp_set_state(sk,TCP_ESTABLISHED);
-				reset_msl_timer(sk, TIME_CLOSE, 100);
-				return;
-			}
-			buff->sk = sk;
-			buff->free = 0;
-			buff->len = sizeof(*t1);
-			buff->localroute = sk->localroute;
-			t1 =(struct tcphdr *) buff->data;
-	
-			/*
-			 *	Put in the IP header and routing stuff. 
-			 */
-			tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
-					 IPPROTO_TCP, sk->opt,
-				         sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
-			if (tmp < 0) 
-			{
-				sk->write_seq++;	/* Very important 8) */
-				kfree_skb(buff,FREE_WRITE);
-
-				/*
-				 * Enter FIN_WAIT1 to await completion of
-				 * written out data and ACK to our FIN.
-				 */
-
-				if(sk->state==TCP_ESTABLISHED)
-					tcp_set_state(sk,TCP_FIN_WAIT1);
-				else
-					tcp_set_state(sk,TCP_FIN_WAIT2);
-				reset_msl_timer(sk, TIME_CLOSE,4*sk->rto);
-				if(timeout)
-					tcp_time_wait(sk);
-
-				release_sock(sk);
-				return;
-			}
-
-			t1 =(struct tcphdr *)((char *)t1 +tmp);
-			buff->len += tmp;
-			buff->dev = dev;
-			memcpy(t1, th, sizeof(*t1));
-			t1->seq = ntohl(sk->write_seq);
-			sk->write_seq++;
-			buff->h.seq = sk->write_seq;
-			t1->ack = 1;
-	
-			/* 
-			 *	Ack everything immediately from now on. 
-			 */
-
-			sk->delay_acks = 0;
-			t1->ack_seq = ntohl(sk->acked_seq);
-			t1->window = ntohs(sk->window=tcp_select_window(sk));
-			t1->fin = 1;
-			t1->rst = 0;
-			t1->doff = sizeof(*t1)/4;
-			tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
-
-			tcp_statistics.TcpOutSegs++;
-
-			if (skb_peek(&sk->write_queue) == NULL) 
-			{
-				sk->sent_seq = sk->write_seq;
-				prot->queue_xmit(sk, dev, buff, 0);
-				reset_xmit_timer(sk, TIME_WRITE, sk->rto);
-			} 
-			else 
-			{
-				reset_xmit_timer(sk, TIME_WRITE, sk->rto);
-				if (buff->next != NULL) 
-				{
-					printk("tcp_close: next != NULL\n");
-					skb_unlink(buff);
-				}
-				skb_queue_tail(&sk->write_queue, buff);
-			}
-
-			/*
-			 * If established (normal close), enter FIN_WAIT1.
-			 * If in CLOSE_WAIT, enter LAST_ACK
-			 * If in CLOSING, remain in CLOSING
-			 * otherwise enter FIN_WAIT2
-			 */
-
-			if (sk->state == TCP_ESTABLISHED)
-				tcp_set_state(sk,TCP_FIN_WAIT1);
-			else if (sk->state == TCP_CLOSE_WAIT)
-				tcp_set_state(sk,TCP_LAST_ACK);
-			else if (sk->state != TCP_CLOSING)
-				tcp_set_state(sk,TCP_FIN_WAIT2);
+		if(tcp_close_state(sk)==1)
+		{
+			tcp_send_fin(sk);
+		}
 	}
 	release_sock(sk);
 }
 
 
 /*
- * This routine takes stuff off of the write queue,
- * and puts it in the xmit queue.
+ * 	This routine takes stuff off of the write queue,
+ *	and puts it in the xmit queue. This happens as incoming acks
+ *	open up the remote window for us.
  */
-static void
-tcp_write_xmit(struct sock *sk)
+ 
+static void tcp_write_xmit(struct sock *sk)
 {
 	struct sk_buff *skb;
 
@@ -2848,6 +3016,14 @@
 	if(sk->zapped)
 		return;
 
+	/*
+	 *	Anything on the transmit queue that fits the window can
+	 *	be added providing we are not
+	 *
+	 *	a) retransmitting (Nagle's rule)
+	 *	b) exceeding our congestion window.
+	 */
+	 
 	while((skb = skb_peek(&sk->write_queue)) != NULL &&
 		before(skb->h.seq, sk->window_seq + 1) &&
 		(sk->retransmits == 0 ||
@@ -2857,9 +3033,18 @@
 	{
 		IS_SKB(skb);
 		skb_unlink(skb);
-		/* See if we really need to send the packet. */
+		
+		/*
+		 *	See if we really need to send the packet. 
+		 */
+		 
 		if (before(skb->h.seq, sk->rcv_ack_seq +1)) 
 		{
+			/*
+			 *	This is acked data. We can discard it. This 
+			 *	cannot currently occur.
+			 */
+			 
 			sk->retransmits = 0;
 			kfree_skb(skb, FREE_WRITE);
 			if (!sk->dead) 
@@ -2888,7 +3073,17 @@
 			tcp_send_check(th, sk->saddr, sk->daddr, size, sk);
 
 			sk->sent_seq = skb->h.seq;
+			
+			/*
+			 *	IP manages our queue for some crazy reason
+			 */
+			 
 			sk->prot->queue_xmit(sk, skb->dev, skb, skb->free);
+			
+			/*
+			 *	Again we slide the timer wrongly
+			 */
+			 
 			reset_xmit_timer(sk, TIME_WRITE, sk->rto);
 		}
 	}
@@ -2914,20 +3109,37 @@
 	if(sk->zapped)
 		return(1);	/* Dead, cant ack any more so why bother */
 
+	/*
+	 *	Have we discovered a larger window
+	 */
+	 
 	ack = ntohl(th->ack_seq);
+
 	if (ntohs(th->window) > sk->max_window) 
 	{
   		sk->max_window = ntohs(th->window);
 #ifdef CONFIG_INET_PCTCP
+		/* Hack because we don't send partial packets to non SWS
+		   handling hosts */
 		sk->mss = min(sk->max_window>>1, sk->mtu);
 #else
 		sk->mss = min(sk->max_window, sk->mtu);
 #endif	
 	}
 
+	/*
+	 *	We have dropped back to keepalive timeouts. Thus we have
+	 *	no retransmits pending.
+	 */
+	 
 	if (sk->retransmits && sk->ip_xmit_timeout == TIME_KEEPOPEN)
 	  	sk->retransmits = 0;
 
+	/*
+	 *	If the ack is newer than sent or older than previous acks
+	 *	then we can probably ignore it.
+	 */
+	 
 	if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq)) 
 	{
 		if(sk->debug)
@@ -2941,6 +3153,11 @@
 		{
 			return(0);
 		}
+		
+		/*
+		 *	Restart the keepalive timer.
+		 */
+		 
 		if (sk->keepopen) 
 		{
 			if(sk->ip_xmit_timeout==TIME_KEEPOPEN)
@@ -2949,10 +3166,16 @@
 		return(1);
 	}
 
+	/*
+	 *	If there is data set flag 1
+	 */
+	 
 	if (len != th->doff*4) 
 		flag |= 1;
 
-	/* See if our window has been shrunk. */
+	/*
+	 *	See if our window has been shrunk. 
+	 */
 
 	if (after(sk->window_seq, ack+ntohs(th->window))) 
 	{
@@ -2976,7 +3199,7 @@
 		 *	queue and a smarter send routine when we send all.
 		 */
 	
-		flag |= 4;
+		flag |= 4;	/* Window changed */
 	
 		sk->window_seq = ack + ntohs(th->window);
 		cli();
@@ -3030,9 +3253,16 @@
 		sk->packets_out= 0;
 	}
 
+	/*
+	 *	Update the right hand window edge of the host
+	 */
+	 
 	sk->window_seq = ack + ntohs(th->window);
 
-	/* We don't want too many packets out there. */
+	/*
+	 *	We don't want too many packets out there. 
+	 */
+	 
 	if (sk->ip_xmit_timeout == TIME_WRITE && 
 		sk->cong_window < 2048 && after(ack, sk->rcv_ack_seq)) 
 	{
@@ -3066,6 +3296,10 @@
 		}
 	}
 
+	/*
+	 *	Remember the highest ack received.
+	 */
+	 
 	sk->rcv_ack_seq = ack;
 
 	/*
@@ -3076,10 +3310,15 @@
 
 	if (sk->ip_xmit_timeout == TIME_PROBE0) 
 	{
+		sk->retransmits = 0;	/* Our probe was answered */
+		
+		/*
+		 *	Was it a usable window open ?
+		 */
+		 
   		if (skb_peek(&sk->write_queue) != NULL &&   /* should always be non-null */
 		    ! before (sk->window_seq, sk->write_queue.next->h.seq)) 
 		{
-			sk->retransmits = 0;
 			sk->backoff = 0;
 			
 			/*
@@ -3106,6 +3345,12 @@
 		if (sk->send_head->link3 &&
 		    after(sk->send_head->h.seq, sk->send_head->link3->h.seq)) 
 			printk("INET: tcp.c: *** bug send_list out of order.\n");
+			
+		/*
+		 *	If our packet is before the ack sequence we can
+		 *	discard it as its confirmed to have arrived the other end.
+		 */
+		 
 		if (before(sk->send_head->h.seq, ack+1)) 
 		{
 			struct sk_buff *oskb;	
@@ -3124,7 +3369,7 @@
 				 * retransmissions.
 				 */
 
-				if (sk->send_head->link3)
+				if (sk->send_head->link3)	/* Any more queued retransmits? */
 					sk->retransmits = 1;
 				else
 					sk->retransmits = 0;
@@ -3154,7 +3399,7 @@
 				sk->write_space(sk);
 			oskb = sk->send_head;
 
-			if (!(flag&2)) 
+			if (!(flag&2)) 	/* Not retransmitting */
 			{
 				long m;
 	
@@ -3187,7 +3432,8 @@
 					sk->rto = 20;
 				sk->backoff = 0;
 			}
-			flag |= (2|4);
+			flag |= (2|4);	/* 2 is really more like 'don't adjust the rtt 
+			                   In this case as we just set it up */
 			cli();
 			oskb = sk->send_head;
 			IS_SKB(oskb);
@@ -3235,6 +3481,9 @@
 			 before(sk->write_queue.next->h.seq, sk->rcv_ack_seq + 1))
 			&& sk->packets_out < sk->cong_window) 
 		{
+			/*
+			 *	Add more data to the send queue.
+			 */
 			flag |= 1;
 			tcp_write_xmit(sk);
 		}
@@ -3243,6 +3492,9 @@
  			sk->ack_backlog == 0 &&
  			sk->state != TCP_TIME_WAIT) 
  		{
+ 			/*
+ 			 *	Data to queue but no room.
+ 			 */
  	        	reset_xmit_timer(sk, TIME_PROBE0, sk->rto);
  		}		
 	}
@@ -3276,8 +3528,8 @@
 			break;
 		default:
 			/*
-			 * must check send_head, write_queue, and ack_backlog
-			 * to determine which timeout to use.
+			 * 	Must check send_head, write_queue, and ack_backlog
+			 * 	to determine which timeout to use.
 			 */
 			if (sk->send_head || skb_peek(&sk->write_queue) != NULL || sk->ack_backlog) {
 				reset_xmit_timer(sk, TIME_WRITE, sk->rto);
@@ -3291,6 +3543,11 @@
 		}
 	}
 
+	/*
+	 *	We have nothing queued but space to send. Send any partial
+	 *	packets immediately (end of Nagle rule application).
+	 */
+	 
 	if (sk->packets_out == 0 && sk->partial != NULL &&
 		skb_peek(&sk->write_queue) == NULL && sk->send_head == NULL) 
 	{
@@ -3373,7 +3630,7 @@
 			sk->state_change(sk);
 		if(sk->max_window==0)
 		{
-			sk->max_window=32;
+			sk->max_window=32;	/* Sanity check */
 			sk->mss=min(sk->max_window,sk->mtu);
 		}
 	}
@@ -3563,6 +3820,8 @@
 	 *	We no longer have anyone receiving data on this connection.
 	 */
 
+#ifndef TCP_DONT_RST_SHUTDOWN		 
+
 	if(sk->shutdown & RCV_SHUTDOWN)
 	{
 		/*
@@ -3571,7 +3830,7 @@
 		 *	BSD stacks still have broken keepalives so we want to
 		 *	cope with it.
 		 */
-		 
+
 		if(skb->len)	/* We don't care if its just an ack or
 				   a keepalive/window probe */
 		{
@@ -3611,6 +3870,8 @@
 		}
 	}
 
+#endif
+
 	/*
 	 * 	Now we have to walk the chain, and figure out where this one
 	 * 	goes into it.  This is set up so that the last packet we received
@@ -3623,10 +3884,6 @@
 	 *	
 	 */
 
-	/* 
-	 *	This should start at the last one, and then go around forwards.
-	 */
-
 	if (skb_peek(&sk->receive_queue) == NULL) 	/* Empty queue is easy case */
 	{
 		skb_queue_head(&sk->receive_queue,skb);
@@ -4134,7 +4391,7 @@
 	sk->retransmit_timer.function=&retransmit_timer;
 	sk->retransmit_timer.data = (unsigned long)sk;
 	reset_xmit_timer(sk, TIME_WRITE, sk->rto);	/* Timer for repeating the SYN until an answer */
-	sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES;
+	sk->retransmits = TCP_SYN_RETRIES;
 
 	sk->prot->queue_xmit(sk, dev, buff, 0);  
 	reset_xmit_timer(sk, TIME_WRITE, sk->rto);
@@ -4290,8 +4547,8 @@
 			skb->sk = NULL;
 			kfree_skb(skb,FREE_READ);
 			/*
-			 * We don't release the socket because it was
-			 * never marked in use.
+			 *	We don't release the socket because it was
+			 *	never marked in use.
 			 */
 			return(0);
 		}
@@ -4510,9 +4767,11 @@
 			goto rfc_step6;
 		}
 
-	/* BSD has a funny hack with TIME_WAIT and fast reuse of a port. There is
-	   a more complex suggestion for fixing these reuse issues in RFC1644
-	   but not yet ready for general use. Also see RFC1379.*/
+	/*
+	 *	BSD has a funny hack with TIME_WAIT and fast reuse of a port. There is
+	 *	a more complex suggestion for fixing these reuse issues in RFC1644
+	 *	but not yet ready for general use. Also see RFC1379.
+	 */
 	
 #define BSD_TIME_WAIT
 #ifdef BSD_TIME_WAIT
@@ -4545,9 +4804,11 @@
 #endif	
 	}
 
-	/* We are now in normal data flow (see the step list in the RFC) */
-	/* Note most of these are inline now. I'll inline the lot when
-	   I have time to test it hard and look at what gcc outputs */
+	/*
+	 *	We are now in normal data flow (see the step list in the RFC)
+	 *	Note most of these are inline now. I'll inline the lot when
+	 *	I have time to test it hard and look at what gcc outputs 
+	 */
 	
 	if(!tcp_sequence(sk,th,len,opt,saddr,dev))
 	{
@@ -4640,6 +4901,7 @@
 	/*
 	 *	Write data can still be transmitted/retransmitted in the
 	 *	following states.  If any other state is encountered, return.
+	 *	[listen/close will never occur here anyway]
 	 */
 
 	if (sk->state != TCP_ESTABLISHED && 
@@ -4678,9 +4940,10 @@
 	memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1));
 
 	/*
-	 * Use a previous sequence.
-	 * This should cause the other end to send an ack.
+	 *	Use a previous sequence.
+	 *	This should cause the other end to send an ack.
 	 */
+	 
 	t1->seq = htonl(sk->sent_seq-1);
 	t1->ack = 1; 
 	t1->res1= 0;
@@ -4694,8 +4957,8 @@
 	t1->window = ntohs(tcp_select_window(sk));
 	t1->doff = sizeof(*t1)/4;
 	tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk);
-
-	 /*	Send it and free it.
+	 /*
+	  *	Send it and free it.
    	  *	This will prevent the timer from automatically being restarted.
 	  */
 	sk->prot->queue_xmit(sk, dev, buff, 1);
diff -u --recursive --new-file v1.1.82/linux/net/inet/tcp.h linux/net/inet/tcp.h
--- v1.1.82/linux/net/inet/tcp.h	Wed Nov 30 21:53:48 1994
+++ linux/net/inet/tcp.h	Wed Jan 18 19:11:49 1995
@@ -49,6 +49,7 @@
 #define TCP_TIMEOUT_LEN	(15*60*HZ) /* should be about 15 mins		*/
 #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully 
 				  * close the socket, about 60 seconds	*/
+#define TCP_FIN_TIMEOUT (3*60*HZ) /* BSD style FIN_WAIT2 deadlock breaker */				  
 #define TCP_ACK_TIME	(3*HZ)	/* time to delay before sending an ACK	*/
 #define TCP_DONE_TIME	250	/* maximum time to wait before actually
 				 * destroying a socket			*/
@@ -69,9 +70,15 @@
  *	TCP option
  */
  
-#define TCPOPT_NOP		1
-#define TCPOPT_EOL		0
-#define TCPOPT_MSS		2
+#define TCPOPT_NOP		1	/* Padding */
+#define TCPOPT_EOL		0	/* End of options */
+#define TCPOPT_MSS		2	/* Segment size negotiating */
+/*
+ *	We don't use these yet, but they are for PAWS and big windows
+ */
+#define TCPOPT_WINDOW		3	/* Window scaling */
+#define TCPOPT_TIMESTAMP	8	/* Better RTT estimations/PAWS */
+
 
 /*
  * The next routines deal with comparing 32 bit unsigned ints
