diff -urNwbB linux/Makefile linux.arm/Makefile
--- linux/Makefile	Sun Mar  3 11:42:06 1996
+++ linux.arm/Makefile	Sun Feb 11 13:47:34 1996
@@ -2,7 +2,7 @@
 PATCHLEVEL = 3
 SUBLEVEL = 35
 
-ARCH = i386
+ARCH = arm
 
 .EXPORT_ALL_VARIABLES:
 
@@ -86,13 +86,19 @@
 # Include the make variables (CC, etc...)
 #
 
+# Modified 11/02/96 by Russell King
+#	- Doesn't include the drivers subdirectory as standard.
+#	- Modified the 'nm' line in vmlinux so that we remove local and absolute symbols.
+
 ARCHIVES	=kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o net/network.a
 FILESYSTEMS	=fs/filesystems.a
-DRIVERS		=drivers/block/block.a \
+DRIVERS		=
+LIBS		=$(TOPDIR)/lib/lib.a
+SUBDIRS		=kernel mm fs net ipc lib
+
+DRIVERS :=drivers/block/block.a \
 		 drivers/char/char.a \
 		 drivers/net/net.a
-LIBS		=$(TOPDIR)/lib/lib.a
-SUBDIRS		=kernel drivers mm fs net ipc lib
 
 ifdef CONFIG_SCSI
 DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
@@ -108,6 +114,10 @@
 
 include arch/$(ARCH)/Makefile
 
+ifndef CONFIG_ARM
+SUBDIRS := drivers $(SUBDIRS)
+endif
+
 .S.s:
 	$(CC) -D__ASSEMBLY__ -traditional -E -o $*.s $<
 .S.o:
@@ -125,7 +135,7 @@
 		$(FILESYSTEMS) \
 		$(DRIVERS) \
 		$(LIBS) -o vmlinux
-	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map
+	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)\|\( A \)\|\( t L\)' | sort > System.map
 
 symlinks:
 	rm -f include/asm
diff -urNwbB linux/arch/arm/Makefile linux.arm/arch/arm/Makefile
--- linux/arch/arm/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/Makefile	Sat Feb 24 13:08:03 1996
@@ -0,0 +1,70 @@
+#
+# arch/arm/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" and "archdep" for cleaning up and making dependencies for
+# this architecture
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Russell King
+#
+
+#
+# Set these to indicate how to link it..
+# -qmagic (we need to remove the 32 byte header for bootup purposes)
+#
+
+SUBARCH		 = -m3
+
+CPP		 = $(CC) -E $(SUBARCH)
+CFLAGS		:= $(CFLAGS:-fomit-frame-pointer=) $(SUBARCH)
+LINKFLAGS	 = -Ttext 0x01800000
+ZLINKFLAGS	 = -N -Ttext 0x01800000
+
+HEAD		:= arch/arm/kernel/head.o
+SUBDIRS		:= arch/arm/drivers arch/arm/kernel arch/arm/mm arch/arm/lib $(SUBDIRS)
+ARCHIVES	:= arch/arm/kernel/kernel.o arch/arm/mm/mm.o arch/arm/lib/lib.o $(ARCHIVES)
+LIBS		:= $(LIBS) `gcc --print-libgcc-file-name`
+
+DRIVERS := arch/arm/drivers/block/block.a arch/arm/drivers/char/char.a \
+           arch/arm/drivers/net/net.a
+
+ifdef CONFIG_SOUND
+DRIVERS := $(DRIVERS) arch/arm/drivers/sound/sound.a
+endif
+
+ifdef CONFIG_SCSI
+DRIVERS := $(DRIVERS) arch/arm/drivers/scsi/scsi.a
+endif
+
+
+arch/arm/kernel: dummy
+	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel
+
+arch/arm/mm: dummy
+	$(MAKE) linuxsubdirs SUBDIRS=arch/arm/mm
+
+MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+
+install:
+	@$(MAKEBOOT) install
+
+zinstall:
+	@$(MAKEBOOT) zinstall
+
+zImage:
+	@$(MAKEBOOT) zImage
+
+archclean:
+	@$(MAKEBOOT) clean
+	@$(MAKE) -C arch/$(ARCH)/drivers/block LINKCLEAN
+	@$(MAKE) -C arch/$(ARCH)/drivers/char LINKCLEAN
+	@$(MAKE) -C arch/$(ARCH)/drivers/net LINKCLEAN
+	@$(MAKE) -C arch/$(ARCH)/drivers/scsi LINKCLEAN
+
+archdep:
+	@$(MAKEBOOT) dep
diff -urNwbB linux/arch/arm/boot/Makefile linux.arm/arch/arm/boot/Makefile
--- linux/arch/arm/boot/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/Makefile	Sun Feb 11 21:59:54 1996
@@ -0,0 +1,47 @@
+#
+# arch/i386/boot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1994 by Linus Torvalds
+#
+
+Image:	$(CONFIGURE) tools/build $(TOPDIR)/vmlinux
+	tools/build $(TOPDIR)/vmlinux > Image
+	sync
+
+zImage:	$(CONFIGURE) tools/build compressed/vmlinux
+	tools/build compressed/vmlinux > zImage
+
+compressed/vmlinux: $(TOPDIR)/vmlinux dep
+	@$(MAKE) -C compressed vmlinux
+
+install: $(CONFIGURE) Image
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+zinstall: $(CONFIGURE) zImage
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+tools/build: tools/build.c
+	$(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include
+
+clean:
+	rm -f Image zImage tools/build
+	@$(MAKE) -C compressed clean
+
+dep:
+
+#zdisk: zImage
+#	dd bs=8192 if=zImage of=/dev/fd0
+
+#zlilo: $(CONFIGURE) zImage
+#	if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
+#	if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
+#	cat zImage > $(INSTALL_PATH)/vmlinuz
+#	cp $(TOPDIR)/System.map $(INSTALL_PATH)/
+#	if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
+
+#install: $(CONFIGURE) zImage
+#	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
diff -urNwbB linux/arch/arm/boot/compressed/Makefile linux.arm/arch/arm/boot/compressed/Makefile
--- linux/arch/arm/boot/compressed/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/Makefile	Sun Mar  3 13:35:37 1996
@@ -0,0 +1,56 @@
+#
+# linux/arch/arm/boot/compressed/Makefile
+#
+# create a compressed vmlinux image from the original vmlinux
+#
+
+CFLAGS = -O2 -DSTDC_HEADERS
+LOADADDR = 0x01800000
+RELADDR = 0x01960000
+
+SYSTEM = $(TOPDIR)/vmlinux
+DECOMP_OBJS = inflate.o unzip.o misc.o ../../lib/ll_char_wr.o
+
+
+all:		vmlinux
+
+vmlinux:	head.o piggy.o
+		$(LD) -Ttext=$(LOADADDR) -o vmlinux head.o piggy.o
+
+head.o: 	head.S
+		$(CC) -traditional -DRELADDR=$(RELADDR) -c head.S
+
+piggy.o:	decomphead.o decompress.a compressed.o
+		$(LD) -s -Ttext=$(RELADDR) -o a.out decomphead.o compressed.o decompress.a
+		strip a.out
+		./piggyback < a.out > piggy.o
+		rm -f a.out
+
+compressed.o:	$(SYSTEM) xtract piggyback
+		./xtract $(SYSTEM) | gzip -9 | ./piggyback > compressed.o
+
+
+# rules for decompression part of kernel
+
+decompress.a:	$(DECOMP_OBJS)
+		$(AR) $(ARFLAGS) decompress.a $(DECOMP_OBJS)
+
+decomphead.o:	decomphead.S
+		$(CC) -traditional -DLOADADDR=$(LOADADDR) -c decomphead.S
+
+../../lib/ll_char_wr.o:
+		make -C ../../lib ll_char_wr.o
+
+
+# rules for extracting & piggybacking the kernel
+
+xtract: 	xtract.c
+		$(HOSTCC) $(CFLAGS) -o xtract xtract.c
+
+piggyback:	piggyback.c
+		$(HOSTCC) $(CFLAGS) -o piggyback piggyback.c
+
+
+clean:
+		rm -f xtract piggyback vmlinux decompress.a a.out
+
diff -urNwbB linux/arch/arm/boot/compressed/crypt.h linux.arm/arch/arm/boot/compressed/crypt.h
--- linux/arch/arm/boot/compressed/crypt.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/crypt.h	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,12 @@
+/* crypt.h (dummy version) -- do not perform encryption
+ * Hardly worth copyrighting :-)
+ */
+
+#ifdef CRYPT
+#  undef CRYPT      /* dummy version */
+#endif
+
+#define RAND_HEAD_LEN  12  /* length of encryption random header */
+
+#define zencode
+#define zdecode
diff -urNwbB linux/arch/arm/boot/compressed/decomphead.S linux.arm/arch/arm/boot/compressed/decomphead.S
--- linux/arch/arm/boot/compressed/decomphead.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/decomphead.S	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,25 @@
+/*
+ *  linux/arch/arm/boot/decomphead.S
+ */
+
+.text
+	.global	__stext
+__stext:
+	b	start
+LC0:
+	.word	__edata
+	.word	__end
+	.word	LOADADDR + 0x23
+	.word	__stack_start+16384
+start:
+	adr	r1, LC0
+	ldmia	r1, {r2, r3, r4, sp}
+	mov	r1, #0
+clrzlp:	str	r1, [r2], #4
+	cmp	r2, r3
+	blt	clrzlp
+	bic	r0, r4, #3
+	bl	_decompress_kernel
+	mov	r0, #0
+	mov	pc, r4
+
diff -urNwbB linux/arch/arm/boot/compressed/gzip.h linux.arm/arch/arm/boot/compressed/gzip.h
--- linux/arch/arm/boot/compressed/gzip.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/gzip.h	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,284 @@
+/* gzip.h -- common declarations for all gzip modules
+ * Copyright (C) 1992-1993 Jean-loup Gailly.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#if defined(__STDC__) || defined(PROTO)
+#  define OF(args)  args
+#else
+#  define OF(args)  ()
+#endif
+
+#ifdef __STDC__
+   typedef void *voidp;
+#else
+   typedef char *voidp;
+#endif
+
+/* I don't like nested includes, but the string functions are used too often */
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#  include <string.h>
+#  define memzero(s, n)     memset ((s), 0, (n))
+#else
+#  include <strings.h>
+#  define strchr            index 
+#  define strrchr           rindex
+#  define memcpy(d, s, n)   bcopy((s), (d), (n)) 
+#  define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) 
+#  define memzero(s, n)     bzero((s), (n))
+#endif
+
+#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
+#  include <memory.h>
+#endif
+
+#ifndef RETSIGTYPE
+#  define RETSIGTYPE void
+#endif
+
+#define local static
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+
+/* Return codes from gzip */
+#define OK      0
+#define ERROR   1
+#define WARNING 2
+
+/* Compression methods (see algorithm.doc) */
+#define STORED     0
+#define COMPRESSED 1
+#define PACKED     2
+/* methods 3 to 7 reserved */
+#define DEFLATED   8
+extern int method;         /* compression method */
+
+/* To save memory for 16 bit systems, some arrays are overlayed between
+ * the various modules:
+ * deflate:  prev+head   window      d_buf  l_buf  outbuf
+ * unlzw:    tab_prefix  tab_suffix  stack  inbuf  outbuf
+ * inflate:              window             inbuf
+ * unpack:               window             inbuf
+ * For compression, input is done in window[]. For decompression, output
+ * is done in window except for unlzw.
+ */
+
+#ifndef	INBUFSIZ
+#  define INBUFSIZ  0x8000  /* input buffer size */
+#endif
+#define INBUF_EXTRA  64     /* required by unlzw() */
+
+#ifndef	OUTBUFSIZ
+#  define OUTBUFSIZ  16384  /* output buffer size */
+#endif
+#define OUTBUF_EXTRA 2048   /* required by unlzw() */
+
+#define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */
+
+#ifdef DYN_ALLOC
+#  define EXTERN(type, array)  extern type * near array
+#  define DECLARE(type, array, size)  type * near array
+#  define ALLOC(type, array, size) { \
+      array = (type*)fcalloc((unsigned)(((size)+1L)/2), 2*sizeof(type)); \
+      if (array == NULL) error("insufficient memory"); \
+   }
+#  define FREE(array) {if (array != NULL) fcfree(array), array=NULL;}
+#else
+#  define EXTERN(type, array)  extern type array[]
+#  define DECLARE(type, array, size)  type array[size]
+#  define ALLOC(type, array, size)
+#  define FREE(array)
+#endif
+
+EXTERN(uch, inbuf);          /* input buffer */
+EXTERN(uch, outbuf);         /* output buffer */
+EXTERN(ush, d_buf);          /* buffer for distances, see trees.c */
+EXTERN(uch, window);         /* Sliding window and suffix table (unlzw) */
+#define tab_suffix window
+#ifndef MAXSEG_64K
+#  define tab_prefix prev    /* hash link (see deflate.c) */
+#  define head (prev+WSIZE)  /* hash head (see deflate.c) */
+   EXTERN(ush, tab_prefix);  /* prefix code (see unlzw.c) */
+#else
+#  define tab_prefix0 prev
+#  define head tab_prefix1
+   EXTERN(ush, tab_prefix0); /* prefix for even codes */
+   EXTERN(ush, tab_prefix1); /* prefix for odd  codes */
+#endif
+
+extern unsigned insize; /* valid bytes in inbuf */
+extern unsigned inptr;  /* index of next byte to be processed in inbuf */
+extern unsigned outcnt; /* bytes in output buffer */
+
+extern long bytes_in;   /* number of input bytes */
+extern long bytes_out;  /* number of output bytes */
+extern long overhead;   /* number of bytes in gzip header */
+
+#define isize bytes_in
+/* for compatibility with old zip sources (to be cleaned) */
+
+extern int  ifd;        /* input file descriptor */
+extern int  ofd;        /* output file descriptor */
+extern char ifname[];   /* input filename or "stdin" */
+extern char ofname[];   /* output filename or "stdout" */
+
+extern ulg time_stamp;  /* original time stamp (modification time) */
+extern long ifile_size; /* input file size, -1 for devices (debug only) */
+
+extern int exit_code;   /* program exit code */
+
+typedef int file_t;     /* Do not use stdio */
+#define NO_FILE  (-1)   /* in memory compression */
+
+
+#define	GZIP_MAGIC     "\037\213" /* Magic header for gzip files, 1F 8B */
+#define	OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */
+#define	PKZIP_MAGIC  "PK\003\004" /* Magic header for pkzip files */
+#define	PACK_MAGIC     "\037\036" /* Magic header for packed files */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+/* internal file attribute */
+#define UNKNOWN (-1)
+#define BINARY  0
+#define ASCII   1
+
+#ifndef WSIZE
+#  define WSIZE 0x8000     /* window size--must be a power of two, and */
+#endif                     /*  at least 32K for zip's deflate method */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST  (WSIZE-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+extern int decrypt;        /* flag to turn on decryption */
+extern int save_orig_name; /* set if original name must be saved */
+extern int verbose;        /* be verbose (-v) */
+extern int level;          /* compression level */
+extern int test;           /* check .z file integrity */
+extern int to_stdout;      /* output to stdout (-c) */
+
+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+/* put_byte is used for the compressed output, put_char for the
+ * uncompressed output. However unlzw() uses window for its
+ * suffix table instead of its output buffer, so it does not use put_char.
+ * (to be cleaned up).
+ */
+#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\
+   flush_outbuf();}
+#define put_char(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\
+   flush_window();}
+
+/* Output a 16 bit value, lsb first */
+#define put_short(w) \
+{ if (outcnt < OUTBUFSIZ-2) { \
+    outbuf[outcnt++] = (uch) ((w) & 0xff); \
+    outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \
+  } else { \
+    put_byte((uch)((w) & 0xff)); \
+    put_byte((uch)((ush)(w) >> 8)); \
+  } \
+}
+
+/* Output a 32 bit value to the bit stream, lsb first */
+#define put_long(n) { \
+    put_short((n) & 0xffff); \
+    put_short(((ulg)(n)) >> 16); \
+}
+
+#define seekable()    0  /* force sequential output */
+#define translate_eol 0  /* no option -a yet */
+
+#define tolow(c)  (isupper(c) ? (c)-'A'+'a' : (c))    /* force to lower case */
+
+/* Macros for getting two-byte and four-byte header values */
+#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8))
+#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16))
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+	/* in zip.c: */
+extern void zip    OF((int in, int out));
+extern int file_read  OF((char *buf,  unsigned size));
+
+	/* in unzip.c */
+extern void unzip        OF((int in, int out));
+extern int check_zipfile OF((int in));
+
+	/* in unpack.c */
+extern void unpack        OF((int in, int out));
+
+	/* in gzip.c */
+RETSIGTYPE abort_gzip   OF((void));
+
+        /* in deflate.c */
+void lm_init OF((int pack_level, ush *flags));
+ulg  deflate OF((void));
+
+        /* in trees.c */
+void ct_init     OF((ush *attr, int *method));
+int  ct_tally    OF((int dist, int lc));
+ulg  flush_block OF((char *buf, ulg stored_len, int eof));
+
+        /* in bits.c */
+void     bi_init    OF((file_t zipfile));
+void     send_bits  OF((int value, int length));
+unsigned bi_reverse OF((unsigned value, int length));
+void     bi_windup  OF((void));
+void     copy_block OF((char *buf, unsigned len, int header));
+extern   int (*read_buf) OF((char *buf, unsigned size));
+
+	/* in util.c: */
+extern ulg  updcrc        OF((uch *s, unsigned n));
+extern void clear_bufs    OF((void));
+extern int  fill_inbuf    OF((void));
+extern void flush_outbuf  OF((void));
+extern void flush_window  OF((void));
+extern char *strlwr       OF((char *s));
+extern char *basename     OF((char *fname));
+extern char *add_envopt   OF((int *argcp, char ***argvp, char *env));
+extern void error         OF((char *m));
+extern void warn          OF((char *a, char *b));
+extern void read_error    OF((void));
+extern void write_error   OF((void));
+extern void display_ratio OF((long num, long den));
+extern voidp xmalloc      OF((unsigned int size));
+
+	/* in inflate.c */
+extern int inflate OF((void));
diff -urNwbB linux/arch/arm/boot/compressed/head.S linux.arm/arch/arm/boot/compressed/head.S
--- linux/arch/arm/boot/compressed/head.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/head.S	Sun Feb 11 09:32:10 1996
@@ -0,0 +1,36 @@
+		.text
+		.align
+
+@ Entry point
+@ r0 = kernel info page
+@ r1 = kernel length (length of this code+data)
+
+		.global __stext
+__stext:
+		b	start
+LC0:
+		.word	_input_data
+		.word	_input_end
+		.word	RELADDR
+start:
+		teq	r0, #0
+		beq	newparams
+		mov	r4,     #0x02000000
+		add	r4, r4, #0x0007C000	@ = 0x0207C000 [new param location]
+		mov	r3,     #0x4000
+		sub	r3, r3, #4
+lp2:		ldmia	r0!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		stmia	r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		subs	r3, r3, #32
+		bpl	lp2
+newparams:	adr	r3, LC0
+		ldmia	r3, {r2, r3, r4}
+		sub	r3, r3, r2
+		mov	r1, r4
+lp:		ldmia	r2!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		stmia	r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		subs	r3, r3, #32
+		bpl	lp
+		ldr	r1, [r1, #0x14]
+		mov	pc, r1
+
diff -urNwbB linux/arch/arm/boot/compressed/inflate.c linux.arm/arch/arm/boot/compressed/inflate.c
--- linux/arch/arm/boot/compressed/inflate.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/inflate.c	Sat Feb 24 09:36:43 1996
@@ -0,0 +1,810 @@
+#define DEBG(x)
+#define DEBG1(x)
+/* inflate.c -- Not copyrighted 1992 by Mark Adler
+   version c10p1, 10 January 1993 */
+
+/* 
+ * Adapted for booting Linux by Hannu Savolainen 1993
+ * based on gzip-1.0.3 
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $";
+#endif
+
+#include "gzip.h"
+#define slide window
+
+#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H)
+#  include <sys/types.h>
+#  include <stdlib.h>
+#endif
+
+struct huft {
+  uch e;                /* number of extra bits or operation */
+  uch b;                /* number of bits in this code or subcode */
+  union {
+    ush n;              /* literal, length base, or distance base */
+    struct huft *t;     /* pointer to next level of table */
+  } v;
+};
+
+
+/* Function prototypes */
+int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *,
+                   struct huft **, int *));
+int huft_free OF((struct huft *));
+int inflate_codes OF((struct huft *, struct huft *, int, int));
+int inflate_stored OF((void));
+int inflate_fixed OF((void));
+int inflate_dynamic OF((void));
+int inflate_block OF((int *));
+int inflate OF((void));
+
+
+#define wp outcnt
+#define flush_output(w) (wp=(w),flush_window())
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+static unsigned border[] = {    /* Order of the bit length code lengths */
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+static ush cplens[] = {         /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* note: see note #13 above about the 258 in this list. */
+static ush cplext[] = {         /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
+static ush cpdist[] = {         /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+static ush cpdext[] = {         /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+
+ulg bb;                         /* bit buffer */
+unsigned bk;                    /* bits in bit buffer */
+
+ush mask_bits[] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+#ifdef CRYPT
+  uch cc;
+#  define NEXTBYTE() \
+     (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte())
+#else
+#  define NEXTBYTE()  (uch)get_byte()
+#endif
+#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}}
+#define DUMPBITS(n) {b>>=(n);k-=(n);}
+
+int lbits = 9;          /* bits in base literal/length lookup table */
+int dbits = 6;          /* bits in base distance lookup table */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16         /* maximum bit length of any code (16 for explode) */
+#define N_MAX 288       /* maximum number of codes in any set */
+
+
+unsigned hufts;         /* track memory usage */
+
+
+int huft_build(b, n, s, d, e, t, m)
+unsigned *b;            /* code lengths in bits (all assumed <= BMAX) */
+unsigned n;             /* number of codes (assumed <= N_MAX) */
+unsigned s;             /* number of simple-valued codes (0..s-1) */
+ush *d;                 /* list of base values for non-simple codes */
+ush *e;                 /* list of extra bits for non-simple codes */
+struct huft **t;        /* result: starting table */
+int *m;                 /* maximum lookup bits, returns actual */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return zero on success, one if
+   the given code set is incomplete (the tables are still built in this
+   case), two if the input is invalid (all zero length codes or an
+   oversubscribed set of lengths), and three if not enough memory. */
+{
+  unsigned a;                   /* counter for codes of length k */
+  unsigned c[BMAX+1];           /* bit length count table */
+  unsigned f;                   /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register unsigned i;          /* counter, current code */
+  register unsigned j;          /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  register unsigned *p;         /* pointer into c[], b[], or v[] */
+  register struct huft *q;      /* points to current table */
+  struct huft r;                /* table entry for structure assignment */
+  struct huft *u[BMAX];         /* table stack */
+  unsigned v[N_MAX];            /* values in order of bit length */
+  register int w;               /* bits before this table == (l * h) */
+  unsigned x[BMAX+1];           /* bit offsets, then code stack */
+  unsigned *xp;                 /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  unsigned z;                   /* number of entries in current table */
+
+DEBG("huft1 ");
+
+  /* Generate counts for each bit length */
+  memzero(c, sizeof(c));
+  p = b;  i = n;
+  do {
+    c[*p++]++;                  /* assume all entries <= BMAX */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (struct huft *)NULL;
+    *m = 0;
+    return 0;
+  }
+
+DEBG("huft2 ");
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((unsigned)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((unsigned)l > i)
+    l = i;
+  *m = l;
+
+DEBG("huft3 ");
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return 2;                 /* bad input: more codes than bits */
+  if ((y -= c[i]) < 0)
+    return 2;
+  c[i] += y;
+
+DEBG("huft4 ");
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+DEBG("huft5 ");
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+
+DEBG("h6 ");
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (struct huft *)NULL;   /* just to keep compilers happy */
+  q = (struct huft *)NULL;      /* ditto */
+  z = 0;                        /* ditto */
+DEBG("h6a ");
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+DEBG("h6b ");
+    a = c[k];
+    while (a--)
+    {
+DEBG("h6b1 ");
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+DEBG1("1 ");
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = (z = g - w) > (unsigned)l ? l : z;  /* upper limit on table size */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+DEBG1("2 ");
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          while (++j < z)       /* try smaller tables up to z bits */
+          {
+            if ((f <<= 1) <= *++xp)
+              break;            /* enough codes to use up j bits */
+            f -= *xp;           /* else deduct codes from patterns */
+          }
+        }
+DEBG1("3 ");
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate and link in new table */
+        q = (struct huft *)malloc((z + 1)*sizeof(struct huft));
+DEBG1("4 ");
+        hufts += z + 1;         /* track memory usage */
+        *t = q + 1;             /* link to list for huft_free() */
+        *(t = &(q->v.t)) = (struct huft *)NULL;
+        u[h] = ++q;             /* table starts after link */
+
+DEBG1("5 ");
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.b = (uch)l;         /* bits to dump before this table */
+          r.e = (uch)(16 + j);  /* bits in this table */
+          r.v.t = q;            /* pointer to this table */
+          j = i >> (w - l);     /* (get around Turbo C bug) */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+DEBG1("6 ");
+      }
+DEBG("h6c ");
+
+      /* set up table entry in r */
+      r.b = (uch)(k - w);
+      if (p >= v + n)
+        r.e = 99;               /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.e = (uch)(*p < 256 ? 16 : 15);    /* 256 is end-of-block code */
+        r.v.n = *p++;           /* simple code is just the value */
+      }
+      else
+      {
+        r.e = (uch)e[*p - s];   /* non-simple--look up in lists */
+        r.v.n = d[*p++ - s];
+      }
+DEBG("h6d ");
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      while ((i & ((1 << w) - 1)) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+      }
+DEBG("h6e ");
+    }
+DEBG("h6f ");
+  }
+
+DEBG("huft7 ");
+
+  /* Return true (1) if we were given an incomplete table */
+  return y != 0 && g != 1;
+}
+
+
+
+int huft_free(t)
+struct huft *t;         /* table to free */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+   list of the tables it made, with the links in a dummy first entry of
+   each table. */
+{
+  register struct huft *p, *q;
+
+
+  /* Go through linked list, freeing from the malloced (t[-1]) address. */
+  p = t;
+  while (p != (struct huft *)NULL)
+  {
+    q = (--p)->v.t;
+    free(p);
+    p = q;
+  } 
+  return 0;
+}
+
+
+int inflate_codes(tl, td, bl, bd)
+struct huft *tl, *td;   /* literal/length and distance decoder tables */
+int bl, bd;             /* number of bits decoded by tl[] and td[] */
+/* inflate (decompress) the codes in a deflated (compressed) block.
+   Return an error code or zero if it all goes ok. */
+{
+  register unsigned e;  /* table entry flag/number of extra bits */
+  unsigned n, d;        /* length and index for copy */
+  unsigned w;           /* current window position */
+  struct huft *t;       /* pointer to table entry */
+  unsigned ml, md;      /* masks for bl and bd bits */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+  /* inflate the coded data */
+  ml = mask_bits[bl];           /* precompute masks for speed */
+  md = mask_bits[bd];
+  for (;;)                      /* do until end of block */
+  {
+    NEEDBITS((unsigned)bl)
+    if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
+      do {
+        if (e == 99)
+          return 1;
+        DUMPBITS(t->b)
+        e -= 16;
+        NEEDBITS(e)
+      } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+    DUMPBITS(t->b)
+    if (e == 16)                /* then it's a literal */
+    {
+      slide[w++] = (uch)t->v.n;
+      if (w == WSIZE)
+      {
+        flush_output(w);
+        w = 0;
+      }
+    }
+    else                        /* it's an EOB or a length */
+    {
+      /* exit if end of block */
+      if (e == 15)
+        break;
+
+      /* get length of block to copy */
+      NEEDBITS(e)
+      n = t->v.n + ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e);
+
+      /* decode distance of block to copy */
+      NEEDBITS((unsigned)bd)
+      if ((e = (t = td + ((unsigned)b & md))->e) > 16)
+        do {
+          if (e == 99)
+            return 1;
+          DUMPBITS(t->b)
+          e -= 16;
+          NEEDBITS(e)
+        } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+      DUMPBITS(t->b)
+      NEEDBITS(e)
+      d = w - t->v.n - ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e)
+
+      /* do the copy */
+      do {
+        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
+#if !defined(NOMEMCPY) && !defined(DEBUG)
+        if (w - d >= e)         /* (this test assumes unsigned comparison) */
+        {
+          memcpy(slide + w, slide + d, e);
+          w += e;
+          d += e;
+        }
+        else                      /* do it slow to avoid memcpy() overlap */
+#endif /* !NOMEMCPY */
+          do {
+            slide[w++] = slide[d++];
+          } while (--e);
+        if (w == WSIZE)
+        {
+          flush_output(w);
+          w = 0;
+        }
+      } while (n);
+    }
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+
+  /* done */
+  return 0;
+}
+
+
+
+int inflate_stored()
+/* "decompress" an inflated type 0 (stored) block. */
+{
+  unsigned n;           /* number of bytes in block */
+  unsigned w;           /* current window position */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+DEBG("<stor");
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+
+  /* go to byte boundary */
+  n = k & 7;
+  DUMPBITS(n);
+
+
+  /* get the length and its complement */
+  NEEDBITS(16)
+  n = ((unsigned)b & 0xffff);
+  DUMPBITS(16)
+  NEEDBITS(16)
+  if (n != (unsigned)((~b) & 0xffff))
+    return 1;                   /* error in compressed data */
+  DUMPBITS(16)
+
+
+  /* read and output the compressed data */
+  while (n--)
+  {
+    NEEDBITS(8)
+    slide[w++] = (uch)b;
+    if (w == WSIZE)
+    {
+      flush_output(w);
+      w = 0;
+    }
+    DUMPBITS(8)
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+
+  DEBG(">");
+  return 0;
+}
+
+
+
+int inflate_fixed()
+/* decompress an inflated type 1 (fixed Huffman codes) block.  We should
+   either replace this with a custom decoder, or at least precompute the
+   Huffman tables. */
+{
+  int i;                /* temporary variable */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned l[288];      /* length list for huft_build */
+
+DEBG("<fix");
+
+  /* set up literal table */
+  for (i = 0; i < 144; i++)
+    l[i] = 8;
+  for (; i < 256; i++)
+    l[i] = 9;
+  for (; i < 280; i++)
+    l[i] = 7;
+  for (; i < 288; i++)          /* make a complete, but wrong code set */
+    l[i] = 8;
+  bl = 7;
+  if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
+    return i;
+
+
+  /* set up distance table */
+  for (i = 0; i < 30; i++)      /* make an incomplete code set */
+    l[i] = 5;
+  bd = 5;
+  if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
+  {
+    huft_free(tl);
+
+    DEBG(">");
+    return i;
+  }
+
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+  return 0;
+}
+
+
+
+int inflate_dynamic()
+/* decompress an inflated type 2 (dynamic Huffman codes) block. */
+{
+  int i;                /* temporary variables */
+  unsigned j;
+  unsigned l;           /* last length */
+  unsigned m;           /* mask for bit lengths table */
+  unsigned n;           /* number of lengths to get */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned nb;          /* number of bit length codes */
+  unsigned nl;          /* number of literal/length codes */
+  unsigned nd;          /* number of distance codes */
+#ifdef PKZIP_BUG_WORKAROUND
+  unsigned ll[288+32];  /* literal/length and distance code lengths */
+#else
+  unsigned ll[286+30];  /* literal/length and distance code lengths */
+#endif
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+DEBG("<dyn");
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in table lengths */
+  NEEDBITS(5)
+  nl = 257 + ((unsigned)b & 0x1f);      /* number of literal/length codes */
+  DUMPBITS(5)
+  NEEDBITS(5)
+  nd = 1 + ((unsigned)b & 0x1f);        /* number of distance codes */
+  DUMPBITS(5)
+  NEEDBITS(4)
+  nb = 4 + ((unsigned)b & 0xf);         /* number of bit length codes */
+  DUMPBITS(4)
+#ifdef PKZIP_BUG_WORKAROUND
+  if (nl > 288 || nd > 32)
+#else
+  if (nl > 286 || nd > 30)
+#endif
+    return 1;                   /* bad lengths */
+
+DEBG("dyn1 ");
+
+  /* read in bit-length-code lengths */
+  for (j = 0; j < nb; j++)
+  {
+    NEEDBITS(3)
+    ll[border[j]] = (unsigned)b & 7;
+    DUMPBITS(3)
+  }
+  for (; j < 19; j++)
+    ll[border[j]] = 0;
+
+DEBG("dyn2 ");
+
+  /* build decoding table for trees--single level, 7 bit lookup */
+  bl = 7;
+  if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
+  {
+    if (i == 1)
+      huft_free(tl);
+    return i;                   /* incomplete code set */
+  }
+
+DEBG("dyn3 ");
+
+  /* read in literal and distance code lengths */
+  n = nl + nd;
+  m = mask_bits[bl];
+  i = l = 0;
+  while ((unsigned)i < n)
+  {
+    NEEDBITS((unsigned)bl)
+    j = (td = tl + ((unsigned)b & m))->b;
+    DUMPBITS(j)
+    j = td->v.n;
+    if (j < 16)                 /* length of code in bits (0..15) */
+      ll[i++] = l = j;          /* save last length in l */
+    else if (j == 16)           /* repeat last length 3 to 6 times */
+    {
+      NEEDBITS(2)
+      j = 3 + ((unsigned)b & 3);
+      DUMPBITS(2)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = l;
+    }
+    else if (j == 17)           /* 3 to 10 zero length codes */
+    {
+      NEEDBITS(3)
+      j = 3 + ((unsigned)b & 7);
+      DUMPBITS(3)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+    else                        /* j == 18: 11 to 138 zero length codes */
+    {
+      NEEDBITS(7)
+      j = 11 + ((unsigned)b & 0x7f);
+      DUMPBITS(7)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+  }
+
+DEBG("dyn4 ");
+
+  /* free decoding table for trees */
+  huft_free(tl);
+
+DEBG("dyn5 ");
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+DEBG("dyn5a ");
+
+  /* build the decoding tables for literal/length and distance codes */
+  bl = lbits;
+  if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0)
+  {
+DEBG("dyn5b ");
+    if (i == 1) {
+      error(" incomplete literal tree\n");
+      huft_free(tl);
+    }
+    return i;                   /* incomplete code set */
+  }
+DEBG("dyn5c ");
+  bd = dbits;
+  if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0)
+  {
+DEBG("dyn5d ");
+    if (i == 1) {
+      error(" incomplete distance tree\n");
+#ifdef PKZIP_BUG_WORKAROUND
+      i = 0;
+    }
+#else
+      huft_free(td);
+    }
+    huft_free(tl);
+    return i;                   /* incomplete code set */
+#endif
+  }
+
+DEBG("dyn6 ");
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+DEBG("dyn7 ");
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+
+  DEBG(">");
+  return 0;
+}
+
+
+
+int inflate_block(e)
+int *e;                 /* last block flag */
+/* decompress an inflated block */
+{
+  unsigned t;           /* block type */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+  DEBG("<blk");
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in last block bit */
+  NEEDBITS(1)
+  *e = (int)b & 1;
+  DUMPBITS(1)
+
+
+  /* read in block type */
+  NEEDBITS(2)
+  t = (unsigned)b & 3;
+  DUMPBITS(2)
+
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+  /* inflate that block type */
+  if (t == 2)
+    return inflate_dynamic();
+  if (t == 0)
+    return inflate_stored();
+  if (t == 1)
+    return inflate_fixed();
+
+  DEBG(">");
+
+  /* bad block type */
+  return 2;
+}
+
+
+
+int inflate()
+/* decompress an inflated entry */
+{
+  int e;                /* last block flag */
+  int r;                /* result code */
+  unsigned h;           /* maximum struct huft's malloc'ed */
+
+
+  /* initialize window, bit buffer */
+  wp = 0;
+  bk = 0;
+  bb = 0;
+
+
+  /* decompress until the last block */
+  h = 0;
+  do {
+    hufts = 0;
+    if ((r = inflate_block(&e)) != 0)
+      return r;
+    if (hufts > h)
+      h = hufts;
+  } while (!e);
+
+  /* Undo too much lookahead. The next read will be byte aligned so we
+   * can discard unused bits in the last meaningful byte.
+   */
+  while (bk >= 8) {
+    bk -= 8;
+    inptr--;
+  }
+
+  /* flush out slide */
+  flush_output(wp);
+
+
+  /* return success */
+#ifdef DEBUG
+  fprintf(stderr, "<%u> ", h);
+#endif /* DEBUG */
+  return 0;
+}
diff -urNwbB linux/arch/arm/boot/compressed/lzw.h linux.arm/arch/arm/boot/compressed/lzw.h
--- linux/arch/arm/boot/compressed/lzw.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/lzw.h	Sun Feb 11 09:32:11 1996
@@ -0,0 +1,42 @@
+/* lzw.h -- define the lzw functions.
+ * Copyright (C) 1992-1993 Jean-loup Gailly.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#if !defined(OF) && defined(lint)
+#  include "gzip.h"
+#endif
+
+#ifndef BITS
+#  define BITS 16
+#endif
+#define INIT_BITS 9              /* Initial number of bits per code */
+
+#define	LZW_MAGIC  "\037\235"   /* Magic header for lzw files, 1F 9D */
+
+#define BIT_MASK    0x1f /* Mask for 'number of compression bits' */
+/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
+ * It's a pity that old uncompress does not check bit 0x20. That makes
+ * extension of the format actually undesirable because old compress
+ * would just crash on the new format instead of giving a meaningful
+ * error message. It does check the number of bits, but it's more
+ * helpful to say "unsupported format, get a new version" than
+ * "can only handle 16 bits".
+ */
+
+#define BLOCK_MODE  0x80
+/* Block compression: if table is full and compression rate is dropping,
+ * clear the dictionary.
+ */
+
+#define LZW_RESERVED 0x60 /* reserved bits */
+
+#define	CLEAR  256       /* flush the dictionary */
+#define FIRST  (CLEAR+1) /* first free entry */
+
+extern int maxbits;      /* max bits per code for LZW */
+extern int block_mode;   /* block compress mode -C compatible with 2.0 */
+
+extern void lzw    OF((int in, int out));
+extern void unlzw  OF((int in, int out));
diff -urNwbB linux/arch/arm/boot/compressed/misc.c linux.arm/arch/arm/boot/compressed/misc.c
--- linux/arch/arm/boot/compressed/misc.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/misc.c	Sun Feb 11 09:32:12 1996
@@ -0,0 +1,534 @@
+/*
+ * misc.c
+ * 
+ * This is a collection of several routines from gzip-1.0.3 
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ * puts by Nick Holloway 1993
+ */
+
+#include "gzip.h"
+#include "lzw.h"
+
+/*#include <asm/segment.h>*/
+
+/*
+ * These are set up by the setup-routine at boot-time:
+ */
+
+struct param_struct
+{
+	unsigned long page_size;
+	unsigned long nr_pages;
+	unsigned long ramdisk_size;
+	unsigned long mountrootrdonly;
+	unsigned long rootdev;
+	unsigned long video_num_cols;
+	unsigned long video_num_rows;
+	unsigned long video_x;
+	unsigned long video_y;
+	unsigned long memc_control_reg;
+	unsigned char sounddefault;
+	unsigned char adfsdrives;
+	unsigned char bytes_per_char_h;
+	unsigned char bytes_per_char_v;
+	unsigned long unused[256/4-11];
+	char paths[8][128];
+	char commandline[256];
+};
+
+static struct param_struct *params=(struct param_struct *)0x0207C000; 
+
+#define EOF -1
+
+DECLARE(uch, inbuf, INBUFSIZ);
+DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
+DECLARE(uch, window, WSIZE);
+
+unsigned outcnt;
+unsigned insize;
+unsigned inptr;
+
+extern char input_data[], input_end[];
+int input_len;
+
+int input_ptr;
+
+int method, exit_code, part_nb, last_member;
+int test = 0;
+int force = 0;
+int verbose = 1;
+long bytes_in, bytes_out;
+
+char *output_data;
+unsigned long output_ptr;
+
+extern int end;
+long free_mem_ptr = (long)&end;
+
+int to_stdout = 0;
+int hard_math = 0;
+
+void (*work)(int inf, int outf);
+void makecrc(void);
+
+local int get_method(int);
+
+char *vidmem;
+int video_num_lines, video_num_columns;
+int bytes_per_char_h;
+int white;
+
+static void puts(const char *);
+
+const char cmap_80[][8]=
+{
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /*   */
+ {0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00}, /* ! */
+ {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, /* " */
+ {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00}, /* # */
+ {0x0C,0x3F,0x68,0x3E,0x0B,0x7E,0x18,0x00}, /* $ */
+ {0x60,0x66,0x0C,0x18,0x30,0x66,0x06,0x00}, /* % */
+ {0x38,0x6C,0x6C,0x38,0x6D,0x66,0x3B,0x00}, /* & */
+ {0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, /* ' */
+ {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, /* ( */
+ {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, /* ) */
+ {0x00,0x18,0x7E,0x3C,0x7E,0x18,0x00,0x00}, /* * */
+ {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00}, /* + */
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, /* , */
+ {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, /* - */
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, /* . */
+ {0x00,0x06,0x0C,0x18,0x30,0x60,0x00,0x00}, /* / */
+ {0x3C,0x66,0x6E,0x7E,0x76,0x66,0x3C,0x00}, /* 0 */
+ {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
+ {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
+ {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
+ {0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
+ {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
+ {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
+ {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
+ {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
+ {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
+ {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x00}, /* : */
+ {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x30}, /* ; */
+ {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, /* < */
+ {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00}, /* = */
+ {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, /* > */
+ {0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00}, /* ? */
+ {0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3C,0x00}, /* @ */
+ {0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
+ {0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
+ {0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
+ {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
+ {0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
+ {0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
+ {0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00}, /* G */
+ {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* H */
+ {0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00}, /* I */
+ {0x3E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}, /* J */
+ {0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00}, /* K */
+ {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}, /* L */
+ {0x63,0x77,0x7F,0x6B,0x6B,0x63,0x63,0x00}, /* M */
+ {0x66,0x66,0x76,0x7E,0x6E,0x66,0x66,0x00}, /* N */
+ {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* O */
+ {0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00}, /* P */
+ {0x3C,0x66,0x66,0x66,0x6A,0x6C,0x36,0x00}, /* Q */
+ {0x7C,0x66,0x66,0x7C,0x6C,0x66,0x66,0x00}, /* R */
+ {0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00}, /* S */
+ {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* T */
+ {0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* U */
+ {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, /* V */
+ {0x63,0x63,0x6B,0x6B,0x7F,0x77,0x63,0x00}, /* W */
+ {0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00}, /* X */
+ {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00}, /* Y */
+ {0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00}, /* Z */
+ {0x7C,0x60,0x60,0x60,0x60,0x60,0x7C,0x00}, /* [ */
+ {0x00,0x60,0x30,0x18,0x0C,0x06,0x00,0x00}, /* \ */
+ {0x3E,0x06,0x06,0x06,0x06,0x06,0x3E,0x00}, /* ] */
+ {0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00}, /* ^ */
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, /* _ */
+ {0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00}, /* ` */
+ {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00}, /* a */
+ {0x60,0x60,0x7C,0x66,0x66,0x66,0x7C,0x00}, /* b */
+ {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, /* c */
+ {0x06,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00}, /* d */
+ {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, /* e */
+ {0x1C,0x30,0x30,0x7C,0x30,0x30,0x30,0x00}, /* f */
+ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x3C}, /* g */
+ {0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x00}, /* h */
+ {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, /* i */
+ {0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x70}, /* j */
+ {0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00}, /* k */
+ {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, /* l */
+ {0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x00}, /* m */
+ {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, /* n */
+ {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, /* o */
+ {0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60}, /* p */
+ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x07}, /* q */
+ {0x00,0x00,0x6C,0x76,0x60,0x60,0x60,0x00}, /* r */
+ {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, /* s */
+ {0x30,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00}, /* t */
+ {0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00}, /* u */
+ {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, /* v */
+ {0x00,0x00,0x63,0x6B,0x6B,0x7F,0x36,0x00}, /* w */
+ {0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00}, /* x */
+ {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x3C}, /* y */
+ {0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00}, /* z */
+ {0x0C,0x18,0x18,0x70,0x18,0x18,0x0C,0x00}, /* { */
+ {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* | */
+ {0x30,0x18,0x18,0x0E,0x18,0x18,0x30,0x00}, /* } */
+ {0x31,0x6B,0x46,0x00,0x00,0x00,0x00,0x00}, /* ~ */
+ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}  /*  */
+};
+
+void *malloc(int size)
+{
+	void *p;
+
+	if (size <0) error("Malloc error\n");
+	if (free_mem_ptr <= 0) error("Memory error\n");
+
+	while(1) {
+		free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */
+
+		p = (void *)free_mem_ptr;
+		free_mem_ptr += size;
+
+		/*
+  		 * The part of the compressed kernel which has already been expanded
+		 * is no longer needed. Therefore we can reuse it for malloc.
+		 * With bigger kernels, this is necessary.
+		 */
+ 
+		if (free_mem_ptr < (long)&end) {
+			if (free_mem_ptr > (long)&input_data[input_ptr])
+				error("\nOut of memory\n");
+
+			return p;
+		}
+		if (free_mem_ptr < 0x01a00000)
+			return p;
+		puts("large kernel, memory tight...");
+		free_mem_ptr = (long)input_data;
+	}
+}
+
+void free(void *where)
+{	/* Don't care */
+}
+
+static void scroll()
+{
+#if 0
+	int i;
+
+	memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
+	for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
+		vidmem[i] = ' ';
+#endif
+}
+
+static void puts(const char *s)
+{
+	extern void ll_char_write(char *, char, char, char, char);
+	int x,y;
+	char c;
+	char *ptr;
+
+	x = params->video_x;
+	y = params->video_y;
+
+	while ( ( c = *s++ ) != '\0' ) {
+		if ( c == '\n' ) {
+			x = 0;
+			if ( ++y >= video_num_lines ) {
+				scroll();
+				y--;
+			}
+		} else {
+			ptr = vidmem + (y*video_num_columns*params->bytes_per_char_v+x)*bytes_per_char_h;
+			ll_char_write(ptr, c, white, 0, 0);
+			if ( ++x >= video_num_columns ) {
+				x = 0;
+				if ( ++y >= video_num_lines ) {
+					scroll();
+					y--;
+				}
+			}
+		}
+	}
+
+	params->video_x = x;
+	params->video_y = y;
+}
+
+__ptr_t memset(__ptr_t s, int c, size_t n)
+{
+	int i;
+	char *ss = (char*)s;
+
+	for (i=0;i<n;i++) ss[i] = c;
+}
+
+__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
+			    size_t __n)
+{
+	int i;
+	char *d = (char *)__dest, *s = (char *)__src;
+
+	for (i=0;i<__n;i++) d[i] = s[i];
+}
+
+extern ulg crc_32_tab[];   /* crc table, defined below */
+
+/* ===========================================================================
+ * Run a set of bytes through the crc shift register.  If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ */
+ulg updcrc(s, n)
+    uch *s;                 /* pointer to bytes to pump through */
+    unsigned n;             /* number of bytes in s[] */
+{
+    register ulg c;         /* temporary variable */
+
+    static ulg crc = (ulg)0xffffffffL; /* shift register contents */
+
+    if (s == NULL) {
+	c = 0xffffffffL;
+    } else {
+	c = crc;
+	while (n--) {
+	    c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
+	}
+    }
+    crc = c;
+    return c ^ 0xffffffffL;       /* (instead of ~c for 64-bit machines) */
+}
+
+/* ===========================================================================
+ * Clear input and output buffers
+ */
+void clear_bufs()
+{
+    outcnt = 0;
+    insize = inptr = 0;
+    bytes_in = bytes_out = 0L;
+}
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+int fill_inbuf()
+{
+    int len, i;
+
+    /* Read as much as possible */
+    insize = 0;
+    do {
+	len = INBUFSIZ-insize;
+	if (len > (input_len-input_ptr+1)) len=input_len-input_ptr+1;
+        if (len == 0 || len == EOF) break;
+
+        for (i=0;i<len;i++) inbuf[insize+i] = input_data[input_ptr+i];
+	insize += len;
+	input_ptr += len;
+    } while (insize < INBUFSIZ);
+
+    if (insize == 0) {
+	error("unable to fill buffer\n");
+    }
+    bytes_in += (ulg)insize;
+    inptr = 1;
+    return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+void flush_window()
+{
+    if (outcnt == 0) return;
+    updcrc(window, outcnt);
+
+    memcpy(&output_data[output_ptr], (char *)window, outcnt);
+
+    bytes_out += (ulg)outcnt;
+    output_ptr += (ulg)outcnt;
+    outcnt = 0;
+}
+
+/*
+ * Code to compute the CRC-32 table. Borrowed from 
+ * gzip-1.0.3/makecrc.c.
+ */
+
+ulg crc_32_tab[256];
+
+void
+makecrc(void)
+{
+/* Not copyrighted 1990 Mark Adler	*/
+
+  unsigned long c;      /* crc shift register */
+  unsigned long e;      /* polynomial exclusive-or pattern */
+  int i;                /* counter for all possible eight bit values */
+  int k;                /* byte being shifted into crc apparatus */
+
+  /* terms of polynomial defining this crc (except x^32): */
+  static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+  /* Make exclusive-or pattern from polynomial */
+  e = 0;
+  for (i = 0; i < sizeof(p)/sizeof(int); i++)
+    e |= 1L << (31 - p[i]);
+
+  crc_32_tab[0] = 0;
+
+  for (i = 1; i < 256; i++)
+  {
+    c = 0;
+    for (k = i | 256; k != 1; k >>= 1)
+    {
+      c = c & 1 ? (c >> 1) ^ e : c >> 1;
+      if (k & 1)
+        c ^= e;
+    }
+    crc_32_tab[i] = c;
+  }
+}
+
+void error(char *x)
+{
+	puts("\n\n");
+	puts(x);
+	puts("\n\n -- System halted");
+
+	while(1);	/* Halt */
+}
+
+#define STACK_SIZE (4096)
+
+long _stack_start [STACK_SIZE];
+
+void decompress_kernel(int output_start)
+{
+	input_len = input_end - input_data;
+	vidmem = (char *)0x02000000;
+
+	video_num_lines = params->video_num_rows;
+	video_num_columns  = params->video_num_cols;
+	bytes_per_char_h = params->bytes_per_char_h;
+	white = bytes_per_char_h == 8 ? 0xfc : 7;
+	
+	if (params->nr_pages * params->page_size < 2048*1024) error("<2M of mem\n");
+
+	output_data = (char *)output_start;	/* Points to kernel start */
+	output_ptr = 0;
+
+	exit_code = 0;
+	test = 0;
+	input_ptr = 0;
+	part_nb = 0;
+
+	clear_bufs();
+	makecrc();
+
+	puts("Uncompressing Linux...");
+
+	method = get_method(0);
+
+	work(0, 0);
+
+	puts("done.\n");
+
+	puts("Now booting the kernel\n");
+}
+
+/* ========================================================================
+ * Check the magic number of the input file and update ofname if an
+ * original name was given and to_stdout is not set.
+ * Return the compression method, -1 for error, -2 for warning.
+ * Set inptr to the offset of the next byte to be processed.
+ * This function may be called repeatedly for an input file consisting
+ * of several contiguous gzip'ed members.
+ * IN assertions: there is at least one remaining compressed member.
+ *   If the member is a zip file, it must be the only one.
+ */
+
+union magic
+{
+	char magic[2];
+	short s_magic;
+};
+
+local int get_method(in)
+    int in;        /* input file descriptor */
+{
+    uch flags;
+    union magic magic; /* magic header */
+    union magic gzip_magic = { GZIP_MAGIC };
+    union magic old_gzip_magic = { OLD_GZIP_MAGIC };
+
+    magic.magic[0] = (char)get_byte();
+    magic.magic[1] = (char)get_byte();
+
+    method = -1;                 /* unknown yet */
+    part_nb++;                   /* number of parts in gzip file */
+    last_member = 0;
+    /* assume multiple members in gzip file except for record oriented I/O */
+
+    if (magic.s_magic == gzip_magic.s_magic
+        || magic.s_magic == old_gzip_magic.s_magic) {
+
+	work = unzip;
+	method = (int)get_byte();
+	flags  = (uch)get_byte();
+
+	if ((flags & ENCRYPTED) != 0)
+		error("Input is encrypted\n");
+
+	if ((flags & CONTINUATION) != 0)
+		error("Multi part input\n");
+
+	if ((flags & RESERVED) != 0) {
+		error("Input has invalid flags\n");
+		exit_code = ERROR;
+		if (force <= 1)
+			return -1;
+	}
+
+	(ulg)get_byte();	/* Get timestamp */
+	((ulg)get_byte()) << 8;
+	((ulg)get_byte()) << 16;
+	((ulg)get_byte()) << 24;
+
+	(void)get_byte();  /* Ignore extra flags for the moment */
+	(void)get_byte();  /* Ignore OS type for the moment */
+
+	if ((flags & EXTRA_FIELD) != 0) {
+	    unsigned len = (unsigned)get_byte();
+	    len |= ((unsigned)get_byte())<<8;
+	    while (len--) (void)get_byte();
+	}
+
+	/* Get original file name if it was truncated */
+	if ((flags & ORIG_NAME) != 0) {
+		if (to_stdout || part_nb > 1) {
+			/* Discard the old name */
+			while (get_byte() != 0) /* null */ ;
+		} else {
+		} /* to_stdout */
+	} /* orig_name */
+
+	/* Discard file comment if any */
+	if ((flags & COMMENT) != 0) {
+		while (get_byte() != 0) /* null */ ;
+	}
+    } else
+	error("unknown compression method");
+    return method;
+}
diff -urNwbB linux/arch/arm/boot/compressed/piggyback.c linux.arm/arch/arm/boot/compressed/piggyback.c
--- linux/arch/arm/boot/compressed/piggyback.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/piggyback.c	Sun Feb 11 09:32:12 1996
@@ -0,0 +1,83 @@
+/*
+ *	linux/zBoot/piggyback.c
+ *
+ *	(C) 1993 Hannu Savolainen
+ */
+
+/*
+ *	This program reads the compressed system image from stdin and
+ *	encapsulates it into an object file written to the stdout.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <a.out.h>
+
+int main(int argc, char *argv[])
+{
+	int c, n=0, len=0;
+	char tmp_buf[512*1024];
+	
+	struct exec obj = {0x00670107};	/* object header */
+	char string_names[] = {"_input_data\0_input_end\0"};
+
+	struct nlist var_names[2] = /* Symbol table */
+		{	
+			{	/* _input_data	*/
+				(char *)4, 7, 0, 0, 0
+			},
+			{	/* _input_len */
+				(char *)16, 7, 0, 0, 0
+			}
+		};
+
+
+	len = 0;
+	while ((n = read(0, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0)
+	      len += n;
+
+	len = (len + 3) & ~3;
+
+	if (n==-1)
+	{
+		perror("stdin");
+		exit(-1);
+	}
+
+	if (len >= sizeof(tmp_buf))
+	{
+		fprintf(stderr, "%s: Input too large\n", argv[0]);
+		exit(-1);
+	}
+
+	fprintf(stderr, "Compressed size %d.\n", len);
+
+/*
+ *	Output object header
+ */
+	obj.a_data = len /* + sizeof(long) */;
+	obj.a_syms = sizeof(var_names);
+	write(1, (char *)&obj, sizeof(obj));
+
+/*
+ *	Output data segment (compressed system & len)
+ */
+	write(1, tmp_buf, len);
+/*	write(1, (char *)&len, sizeof(len));*/
+
+/*
+ *	Output symbol table
+ */
+	var_names[1].n_value = len;
+	write(1, (char *)&var_names, sizeof(var_names));
+
+/*
+ *	Output string table
+ */
+	len = sizeof(string_names) + sizeof(len);
+	write(1, (char *)&len, sizeof(len));
+	write(1, string_names, sizeof(string_names));
+
+	exit(0);
+
+}
diff -urNwbB linux/arch/arm/boot/compressed/unzip.c linux.arm/arch/arm/boot/compressed/unzip.c
--- linux/arch/arm/boot/compressed/unzip.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/unzip.c	Sat Feb 24 09:36:43 1996
@@ -0,0 +1,180 @@
+/* unzip.c -- decompress files in gzip or pkzip format.
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ *
+ * Adapted for Linux booting by Hannu Savolainen 1993
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ *
+ * The code in this file is derived from the file funzip.c written
+ * and put in the public domain by Mark Adler.
+ */
+
+/*
+   This version can extract files in gzip or pkzip format.
+   For the latter, only the first entry is extracted, and it has to be
+   either deflated or stored.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: unzip.c,v 0.9 1993/02/10 16:07:22 jloup Exp $";
+#endif
+
+#include "gzip.h"
+#include "crypt.h"
+
+#include <stdio.h>
+
+/* PKZIP header definitions */
+#define LOCSIG 0x04034b50L      /* four-byte lead-in (lsb first) */
+#define LOCFLG 6                /* offset of bit flag */
+#define  CRPFLG 1               /*  bit for encrypted entry */
+#define  EXTFLG 8               /*  bit for extended local header */
+#define LOCHOW 8                /* offset of compression method */
+#define LOCTIM 10               /* file mod time (for decryption) */
+#define LOCCRC 14               /* offset of crc */
+#define LOCSIZ 18               /* offset of compressed size */
+#define LOCLEN 22               /* offset of uncompressed length */
+#define LOCFIL 26               /* offset of file name field length */
+#define LOCEXT 28               /* offset of extra field length */
+#define LOCHDR 30               /* size of local header, including sig */
+#define EXTHDR 16               /* size of extended local header, inc sig */
+
+
+/* Globals */
+
+int decrypt;      /* flag to turn on decryption */
+char *key;        /* not used--needed to link crypt.c */
+int pkzip = 0;    /* set for a pkzip file */
+int extended = 0; /* set if extended local header */
+
+/* ===========================================================================
+ * Check zip file and advance inptr to the start of the compressed data.
+ * Get ofname from the local header if necessary.
+ */
+int check_zipfile(in)
+    int in;   /* input file descriptors */
+{
+    uch *h = inbuf + inptr; /* first local header */
+
+    /* ifd = in; */
+
+    /* Check validity of local header, and skip name and extra fields */
+    inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);
+
+    if (inptr > insize || LG(h) != LOCSIG) {
+	error("input not a zip");
+    }
+    method = h[LOCHOW];
+    if (method != STORED && method != DEFLATED) {
+	error("first entry not deflated or stored--can't extract");
+    }
+
+    /* If entry encrypted, decrypt and validate encryption header */
+    if ((decrypt = h[LOCFLG] & CRPFLG) != 0) {
+	error("encrypted file\n");
+	exit_code = ERROR;
+	return -1;
+    }
+
+    /* Save flags for unzip() */
+    extended = (h[LOCFLG] & EXTFLG) != 0;
+    pkzip = 1;
+
+    /* Get ofname and time stamp from local header (to be done) */
+    return 0;
+}
+
+/* ===========================================================================
+ * Unzip in to out.  This routine works on both gzip and pkzip files.
+ *
+ * IN assertions: the buffer inbuf contains already the beginning of
+ *   the compressed data, from offsets inptr to insize-1 included.
+ *   The magic header has already been checked. The output buffer is cleared.
+ */
+void unzip(in, out)
+    int in, out;   /* input and output file descriptors */
+{
+    ulg orig_crc = 0;       /* original crc */
+    ulg orig_len = 0;       /* original uncompressed length */
+    int n;
+    uch buf[EXTHDR];        /* extended local header */
+
+    /* ifd = in;
+    ofd = out; */
+
+    updcrc(NULL, 0);           /* initialize crc */
+
+    if (pkzip && !extended) {  /* crc and length at the end otherwise */
+	orig_crc = LG(inbuf + LOCCRC);
+	orig_len = LG(inbuf + LOCLEN);
+    }
+
+    /* Decompress */
+    if (method == DEFLATED)  {
+
+	int res = inflate();
+
+	if (res == 3) {
+	    error("out of memory");
+	} else if (res != 0) {
+	    error("invalid compressed format");
+	}
+
+    } else if (pkzip && method == STORED) {
+
+	register ulg n = LG(inbuf + LOCLEN);
+
+	if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) {
+
+	    error("length mismatch");
+	}
+	while (n--) {
+	    uch c = (uch)get_byte();
+#ifdef CRYPT
+	    if (decrypt) zdecode(c);
+#endif
+	    if (!test) put_char(c);
+	}
+    } else {
+	error("internal error, invalid method");
+    }
+
+    /* Get the crc and original length */
+    if (!pkzip) {
+        /* crc32  (see algorithm.doc)
+	 * uncompressed input size modulo 2^32
+         */
+	for (n = 0; n < 8; n++) {
+	    buf[n] = (uch)get_byte(); /* may cause an error if EOF */
+	}
+	orig_crc = LG(buf);
+	orig_len = LG(buf+4);
+
+    } else if (extended) {  /* If extended header, check it */
+	/* signature - 4bytes: 0x50 0x4b 0x07 0x08
+	 * CRC-32 value
+         * compressed size 4-bytes
+         * uncompressed size 4-bytes
+	 */
+	for (n = 0; n < EXTHDR; n++) {
+	    buf[n] = (uch)get_byte(); /* may cause an error if EOF */
+	}
+	orig_crc = LG(buf+4);
+	orig_len = LG(buf+12);
+    }
+
+    /* Validate decompression */
+    if (orig_crc != updcrc(outbuf, 0)) {
+	error("crc error");
+    }
+    if (orig_len != bytes_out) {
+	error("length error");
+    }
+
+    /* Check if there are more entries in a pkzip file */
+    if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) {
+	    error("zip file has more than one entry");
+    }
+    extended = pkzip = 0; /* for next file */
+}
diff -urNwbB linux/arch/arm/boot/compressed/xtract.c linux.arm/arch/arm/boot/compressed/xtract.c
--- linux/arch/arm/boot/compressed/xtract.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/compressed/xtract.c	Sun Feb 11 09:32:12 1996
@@ -0,0 +1,86 @@
+/*
+ *  linux/zBoot/xtract.c
+ *
+ *  Copyright (C) 1993  Hannu Savolainen
+ *
+ *	Extracts the system image and writes it to the stdout.
+ *	based on tools/build.c by Linus Torvalds
+ */
+
+#include <stdio.h>	/* fprintf */
+#include <string.h>
+#include <stdlib.h>	/* contains exit */
+#include <sys/types.h>	/* unistd.h needs this */
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>	/* contains read/write */
+#include <fcntl.h>
+#include <a.out.h>
+
+#define N_MAGIC_OFFSET 1024
+
+static int GCC_HEADER = sizeof(struct exec);
+
+#define STRINGIFY(x) #x
+
+void die(char * str)
+{
+	fprintf(stderr,"%s\n",str);
+	exit(1);
+}
+
+void usage(void)
+{
+	die("Usage: xtract system [ | gzip | piggyback > piggy.s]");
+}
+
+int main(int argc, char ** argv)
+{
+	int i,c,id, sz;
+	char buf[1024];
+	char major_root, minor_root;
+	struct stat sb;
+
+	struct exec *ex = (struct exec *)buf;
+
+	if (argc  != 2)
+		usage();
+	
+	if ((id=open(argv[1],O_RDONLY,0))<0)
+		die("Unable to open 'system'");
+	if (read(id,buf,GCC_HEADER) != GCC_HEADER)
+		die("Unable to read header of 'system'");
+	if (N_MAGIC(*ex) == ZMAGIC) {
+		GCC_HEADER = N_MAGIC_OFFSET;
+		lseek(id, GCC_HEADER, SEEK_SET);
+	} else if (N_MAGIC(*ex) != QMAGIC)
+		die("Non-GCC header of 'system'");
+
+	sz = N_SYMOFF(*ex) - GCC_HEADER + 4;	/* +4 to get the same result than tools/build */
+
+	fprintf(stderr, "System size is %d\n", sz);
+
+	while (sz)
+	{
+		int l, n;
+
+		l = sz;
+		if (l > sizeof(buf)) l = sizeof(buf);
+
+		if ((n=read(id, buf, l)) !=l)
+		{
+			if (n == -1) 
+			   perror(argv[1]);
+			else
+			   fprintf(stderr, "Unexpected EOF\n");
+
+			die("Can't read system");
+		}
+
+		write(1, buf, l);
+		sz -= l;
+	}
+
+	close(id);
+	return(0);
+}
diff -urNwbB linux/arch/arm/boot/install.sh linux.arm/arch/arm/boot/install.sh
--- linux/arch/arm/boot/install.sh	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/install.sh	Sun Mar  3 12:16:58 1996
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# arch/i386/boot/install.sh
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+#
+# "make install" script for i386 architecture
+#
+# Arguments:
+#   $1 - kernel version
+#   $2 - kernel image file
+#   $3 - kernel map file
+#   $4 - default install path (blank if root directory)
+#
+
+# User may have a custom install script
+
+if [ -x /sbin/installkernel ]; then
+  exec /sbin/installkernel "$@"
+fi
+
+if [ "$2" = "zImage" ]; then
+# Compressed install
+  echo "Installing compressed kernel"
+  if [ -f $4/vmlinuz-$1 ]; then
+    mv $4/vmlinuz-$1 $4/vmlinuz.old
+  fi
+
+  if [ -f $4/System.map-$1 ]; then
+    mv $4/System.map-$1 $4/System.old
+  fi
+
+  cat $2 > $4/vmlinuz-$1
+  cp $3 $4/System.map-$1
+else
+# Normal install
+  echo "Installing normal kernel"
+  if [ -f $4/vmlinux-$1 ]; then
+    mv $4/vmlinux-$1 $4/vmlinux.old
+  fi
+
+  if [ -f $4/System.map ]; then
+    mv $4/System.map $4/System.old
+  fi
+
+  cat $2 > $4/vmlinux-$1
+  cp $3 $4/System.map
+fi
+
+if [ -x /sbin/loadmap ]; then
+  /sbin/loadmap
+else
+  echo "You have to install it yourself"
+fi
diff -urNwbB linux/arch/arm/boot/tools/build.c linux.arm/arch/arm/boot/tools/build.c
--- linux/arch/arm/boot/tools/build.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/tools/build.c	Sun Feb 11 22:01:19 1996
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <a.out.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+	void *data;
+	struct exec ex;
+	FILE *f;
+	int totlen;
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: build kernel-name\n");
+		exit(1);
+	}
+
+	f = fopen(argv[1], "rb");
+
+	fread(&ex, 1, sizeof(ex), f);
+
+	if(N_MAGIC(ex) == ZMAGIC) {
+		fseek(f, 4096, SEEK_SET);
+		totlen = ex.a_text + ex.a_data + ex.a_bss;
+	} else
+	if(N_MAGIC(ex) == QMAGIC) {
+		unsigned long my_header[8];
+		
+		fseek(f, 0x20, SEEK_SET);
+
+		memset(my_header, 0, 0x20);
+
+		my_header[0] = 0xea000006;
+		
+		fwrite(my_header, 4, 8, stdout);
+		
+		totlen = ex.a_text + ex.a_data + ex.a_bss - 0x20;
+	} else {
+		fprintf(stderr, "Unacceptable a.out header on kernel\n");
+		fclose(f);
+		exit(1);
+	}
+
+	fprintf(stderr, "Kernel is %dk (%dk text, %dk data, %dk bss)\n",
+		(ex.a_text + ex.a_data + ex.a_bss)/1024,
+		 ex.a_text/1024, ex.a_data/1024, ex.a_bss/1024);
+
+	data = malloc(totlen);
+	fread(data, 1, totlen, f);
+	fwrite(data, 1, totlen, stdout);
+
+	free(data);
+	fclose(f);
+	fflush(stdout);
+	return 0;
+}
diff -urNwbB linux/arch/arm/config.in linux.arm/arch/arm/config.in
--- linux/arch/arm/config.in	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/config.in	Sat Feb 24 21:32:58 1996
@@ -0,0 +1,202 @@
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+# This is to tell configure not to execute the sound configuration.
+#
+CONFIG_ARM=1
+
+mainmenu_name "Linux kernel configuration"
+
+echo "/* Automatically generated by configure.  Use Make config to change */" >> $CONFIG_H
+#
+# These are to alter the behaviour of various Makefiles and init/main.c
+# in the architecture-independent areas.
+#
+echo "#define CONFIG_ARM 1" >> $CONFIG_H
+echo "CONFIG_ARM=1" >> $CONFIG
+
+mainmenu_option next_comment
+comment 'General setup'
+bool 'Compile for arm3' CONFIG_ARM3 y
+if [ "$CONFIG_ARM3" = "n" ]; then
+  bool 'Compile for arm610/710' CONFIG_ARM610 y
+  if [ "$CONFIG_ARM610" = "n" ]; then
+    bool 'Compile for StrongARM' CONFIG_ARMSTRONG y
+  fi
+else
+  bool 'Page size as 32K' CONFIG_PAGE_32K y
+  if [ "$CONFIG_PAGE_32K" = "n" ]; then
+    bool 'Page size as 16K' CONFIG_PAGE_16K y
+    if [ "$CONFIG_PAGE_16K" = "n" ]; then
+      echo "Auto detected page size"
+    fi
+  fi
+fi
+
+comment 'Math emulation is provided by a run-time loadable module'
+
+tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
+bool 'Normal IDE disk/cdrom support' CONFIG_ST506 y
+if [ "$CONFIG_ST506" = "y" ]; then
+  comment 'Please see drivers/block/README.ide for help/info on IDE drives'
+  bool '   Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD y
+#  if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then
+#    bool '   Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n
+#  else
+#    bool '   Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE y
+#  fi
+#  if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+#    bool '   Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n
+#  fi
+fi
+tristate 'MFM harddisk support' CONFIG_BLK_DEV_XD n
+
+bool 'Networking support' CONFIG_NET y
+bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
+bool 'System V IPC' CONFIG_SYSVIPC y
+#tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n
+
+mainmenu_option next_comment
+comment 'Loadable module support'
+bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS y
+
+if [ "$CONFIG_NET" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'Networking options'
+  bool 'TCP/IP networking' CONFIG_INET y
+  if [ "$CONFIG_INET" = "y" ]; then
+    bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n
+    bool 'IP: multicasting' CONFIG_IP_MULTICAST n
+    bool 'IP: firewalling' CONFIG_IP_FIREWALL n
+    bool 'IP: accounting' CONFIG_IP_ACCT n
+    tristate 'IP: tunneling' CONFIG_NET_IPIP n
+    if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then
+      bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y
+      bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n
+    fi
+    comment '(it is safe to leave these untouched)'
+    bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n
+    tristate 'IP: Reverse ARP' CONFIG_INET_RARP n
+    bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y
+    bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
+    bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y
+    bool 'IP: Allow large windows (not recommended if <16MB of memory)' CONFIG_SKB_LARGE n
+  fi
+  bool 'The IPX protocol' CONFIG_IPX n
+  bool 'Appletalk DDP' CONFIG_ATALK n
+  bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
+  if [ "$CONFIG_AX25" = "y" ]; then
+    bool 'Amateur Radio NET/ROM' CONFIG_NETROM n
+  fi
+  bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK n
+  if [ "$CONFIG_NETLINK" = "y" ]; then
+    bool 'Routing messages' CONFIG_RTNETLINK y
+  fi
+fi
+
+mainmenu_option next_comment
+comment 'SCSI support'
+
+tristate 'SCSI support?' CONFIG_SCSI n
+
+if [ "$CONFIG_SCSI" = "n" ]; then
+
+  comment 'Skipping SCSI configuration options...'
+
+else
+
+  comment 'SCSI support type (disk, tape, CDrom)'
+
+  dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD y $CONFIG_SCSI
+  dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST y $CONFIG_SCSI
+  dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR y $CONFIG_SCSI
+  dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG n $CONFIG_SCSI
+
+  comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
+
+  bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n
+
+  mainmenu_option next_comment
+  comment 'SCSI low-level drivers'
+
+  dep_tristate 'Acorn SCSI card (aka30) support' CONFIG_SCSI_ACORNSCSI_3 y $CONFIG_SCSI
+  dep_tristate 'Cumana 1 support' CONFIG_SCSI_CUMANA_1 n $CONFIG_SCSI
+  dep_tristate 'EcoScsi support' CONFIG_SCSI_ECOSCSI n $CONFIG_SCSI
+  dep_tristate 'Oak SCSI support' CONFIG_SCSI_OAK n $CONFIG_SCSI
+  dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n $CONFIG_SCSI
+fi
+
+if [ "$CONFIG_NET" = "y" ]; then
+
+  mainmenu_option next_comment
+  comment 'Network device support'
+
+  bool 'Network device support?' CONFIG_NETDEVICES y
+  if [ "$CONFIG_NETDEVICES" = "n" ]; then
+
+  comment 'Skipping network driver configuration options...'
+
+  else
+    tristate 'Dummy net driver support' CONFIG_DUMMY y
+    tristate 'SLIP (serial line) support' CONFIG_SLIP y
+    if [ "$CONFIG_SLIP" = "y" ]; then
+      bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
+    fi
+    tristate 'PPP (point-to-point) support' CONFIG_PPP y
+    if [ ! "$CONFIG_PPP" = "n" ]; then
+      comment 'CCP compressors for PPP are only built as modules.'
+    fi
+    tristate 'PLIP (parallel port) support' CONFIG_PLIP y
+    tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n
+    tristate 'Ether1 (82586) support' CONFIG_ETHER1 n
+    tristate 'Ether3 (NQ8005) support' CONFIG_ETHER3 n
+  fi
+fi
+
+mainmenu_option next_comment
+comment 'Filesystems'
+
+tristate 'Standard (minix) fs support' CONFIG_MINIX_FS n
+tristate 'Extended fs support' CONFIG_EXT_FS n
+tristate 'Second extended fs support' CONFIG_EXT2_FS y
+tristate 'xiafs filesystem support' CONFIG_XIA_FS n
+tristate 'msdos fs support' CONFIG_MSDOS_FS y
+if [ "$CONFIG_MSDOS_FS" = "y" ]; then
+  tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
+fi
+tristate '/proc filesystem support' CONFIG_PROC_FS y
+tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
+tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
+tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
+if [ "$CONFIG_INET" = "y" ]; then
+  tristate 'NFS filesystem support' CONFIG_NFS_FS y
+  tristate 'SMB filesystem (to mount WfW shares etc...) support' CONFIG_SMB_FS n
+fi
+
+mainmenu_option next_comment
+comment 'Character devices'
+
+tristate 'Parallel printer support' CONFIG_PRINTER y
+tristate 'Mouse support' CONFIG_MOUSE y
+if [ "$CONFIG_MOUSE" = "n" ]; then
+  comment 'Skipping mouse options...'
+else
+  tristate 'Keyboard mouse [all but RiscPC]' CONFIG_KBDMOUSE y
+fi
+
+mainmenu_option next_comment
+comment 'Sound'
+
+tristate 'Sound support' CONFIG_SOUND n
+
+mainmenu_option next_comment
+comment 'Kernel hacking'
+
+bool 'Kernel profiling support' CONFIG_PROFILE n
+if [ "$CONFIG_PROFILE" = "y" ]; then
+  int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+fi
+if [ "$CONFIG_SCSI" = "y" ]; then
+bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y
+fi
diff -urNwbB linux/arch/arm/drivers/Makefile linux.arm/arch/arm/drivers/Makefile
--- linux/arch/arm/drivers/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/Makefile	Sun Feb 11 09:32:13 1996
@@ -0,0 +1,33 @@
+#
+# Makefile for the linux kernel device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+SUB_DIRS     := block char net #streams
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) scsi sound
+
+# If CONFIG_SCSI is set, the core of scsi support will be added to the kernel,
+# but some of the low-level things may also be modules.
+ifeq ($(CONFIG_SCSI),y)
+SUB_DIRS += scsi
+MOD_SUB_DIRS += scsi
+else
+  ifeq ($(CONFIG_SCSI),m)
+  MOD_SUB_DIRS += scsi
+  endif
+endif
+
+ifeq ($(CONFIG_SOUND),y)
+SUB_DIRS += sound
+else
+  ifeq ($(CONFIG_SOUND),m)
+  MOD_SUB_DIRS += sound
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -urNwbB linux/arch/arm/drivers/block/Makefile linux.arm/arch/arm/drivers/block/Makefile
--- linux/arch/arm/drivers/block/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/Makefile	Sat Feb 24 13:17:05 1996
@@ -0,0 +1,63 @@
+#
+# Makefile for the kernel block device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+all: links first_rule
+
+L_TARGET := block.a
+L_OBJS   := ll_rw_blk.o ramdisk.o genhd.o hdsrch.o
+M_OBJS   :=
+MOD_LIST_NAME := BLOCK_MODULES
+
+ifeq ($(CONFIG_BLK_DEV_FD),y)
+L_OBJS += floppy.o
+else
+  ifeq ($(CONFIG_BLK_DEV_FD),m)
+  M_OBJS += floppy.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HD),y)
+L_OBJS += hd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE),y)
+L_OBJS += ide.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MFM),y)
+L_OBJS += mfmhd.o
+else
+  ifeq ($(CONFIG_BLK_DEV_MFM),m)
+  M_OBJS += mfmhd_mod.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+fastdep: links
+
+mfmhd_mod.o: mfmhd.o
+	ld -r -o $@ mfmhd.o `gcc --print-libgcc-file-name`
+
+LK = blk.h ll_rw_blk.c ramdisk.c README.fd README.hd
+
+links:
+	-@for f in $(LK); do \
+		ln -s ../../../../drivers/block/$$f .; \
+	done
+	touch links
+
+LINKCLEAN:
+	-@for f in $(LK); do \
+		if [ -L $$f ]; then rm -f $$f; fi; \
+	done
+	rm -f links
+
diff -urNwbB linux/arch/arm/drivers/block/floppy.c linux.arm/arch/arm/drivers/block/floppy.c
--- linux/arch/arm/drivers/block/floppy.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/floppy.c	Sun Mar  3 12:20:41 1996
@@ -0,0 +1,4120 @@
+/*
+ *  linux/arch/arm/drivers/block/floppy.c
+ *  [ was linux/drivers/block/floppy.c ]
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1993, 1994  Alain Knaff
+ *  Modifications Copyright (C) 1995 Russell King
+ */
+/*
+ * 02.12.91 - Changed to static variables to indicate need for reset
+ * and recalibrate. This makes some things easier (output_byte reset
+ * checking etc), and means less interrupt jumping in case of errors,
+ * so the code is hopefully easier to understand.
+ */
+
+/*
+ * This file is certainly a mess. I've tried my best to get it working,
+ * but I don't like programming floppies, and I have only one anyway.
+ * Urgel. I should check for more errors, and do more graceful error
+ * recovery. Seems there are problems with several drives. I've tried to
+ * correct them. No promises.
+ */
+
+/*
+ * As with hd.c, all routines within this file can (and will) be called
+ * by interrupts, so extreme caution is needed. A hardware interrupt
+ * handler may not sleep, or a kernel panic will happen. Thus I cannot
+ * call "floppy-on" directly, but have to set a special timer interrupt
+ * etc.
+ */
+
+/*
+ * 28.02.92 - made track-buffering routines, based on the routines written
+ * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
+ */
+
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
+ * the floppy-change signal detection.
+ */
+
+/*
+ * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
+ * FDC data overrun bug, added some preliminary stuff for vertical
+ * recording support.
+ *
+ * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
+ *
+ * TODO: Errors are still not counted properly.
+ */
+
+/* 1992/9/20
+ * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
+ * modelled after the freeware MS/DOS program fdformat/88 V1.8 by
+ * Christoph H. Hochst\"atter.
+ * I have fixed the shift values to the ones I always use. Maybe a new
+ * ioctl() should be created to be able to modify them.
+ * There is a bug in the driver that makes it impossible to format a
+ * floppy as the first thing after bootup.
+ */
+
+/*
+ * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
+ * this helped the floppy driver as well. Much cleaner, and still seems to
+ * work.
+ */
+
+/* 1994/6/24 --bbroad-- added the floppy table entries and made
+ * minor modifications to allow 2.88 floppies to be run.
+ */
+
+/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
+ * disk types.
+ */
+
+/*
+ * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
+ * format bug fixes, but unfortunately some new bugs too...
+ */
+
+/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
+ * errors to allow safe writing by specialized programs.
+ */
+
+/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
+ * by defining bit 1 of the "stretch" parameter to mean put sectors on the
+ * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
+ * drives are "upside-down").
+ */
+
+/*
+ * 1995/8/16 -- Russell King -- altered method for turning floppy drives on
+ * for the arm.
+ */
+#define CONFIG_FLOPPY_SANITY
+#undef  CONFIG_FLOPPY_SILENT_DCL_CLEAR
+
+#define DEBUGT 2
+#define DCL_DEBUG /* debug disk change line */
+
+#include <linux/config.h>
+
+/* do print messages for unexpected interrupts */
+static int print_unex=1;
+
+#ifdef MODULE
+#define FD_MODULE
+
+#include <linux/module.h>
+/*
+ * NB. we must include the kernel identification string to install the module.
+ */
+#include <linux/version.h>
+char kernel_version[] = UTS_RELEASE;
+
+int FLOPPY_IRQ=12;
+int FLOPPY_DMA=2;
+int ALLOWED_DRIVE_MASK = 0x33;
+int FDC1 = 0x3f0;
+int FDC2 = -1;
+
+#endif
+
+#ifndef FD_MODULE
+/* the following is the mask of allowed drives. By default units 2 and
+ * 3 of both floppy controllers are disabled, because switching on the
+ * motor of these drives causes system hangs on some PCI computers. drive
+ * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
+ * a drive is allowed. */
+static int ALLOWED_DRIVE_MASK=0x33;
+
+#define FLOPPY_IRQ 12
+#define FLOPPY_DMA 2
+#define FDC1 0x3f0
+static int FDC2=-1;
+#endif
+
+#define MODULE_AWARE_DRIVER
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#define FDPATCHES
+#include <linux/fdreg.h>
+
+/* ============================================ *
+ * old fd.h                                     *
+ * =========================================== */
+
+#define OLDFDCLRPRM 0 /* clear user-defined parameters */
+#define OLDFDSETPRM 1 /* set user-defined parameters for current media */
+#define OLDFDSETMEDIAPRM 1
+#define OLDFDDEFPRM 2 /* set user-defined parameters until explicitly cleared */
+#define OLDFDDEFMEDIAPRM 2
+#define OLDFDGETPRM 3 /* get disk parameters */
+#define OLDFDGETMEDIAPRM 3
+#define OLDFDMSGON  4 /* issue kernel messages on media type change */
+#define OLDFDMSGOFF 5 /* don't issue kernel messages on media type change */
+#define OLDFDFMTBEG 6 /* begin formatting a disk */
+#define OLDFDFMTTRK 7 /* format the specified track */
+#define OLDFDFMTEND 8 /* end formatting a disk */
+#define OLDFDSETEMSGTRESH       10      /* set fdc error reporting threshold */
+#define OLDFDFLUSH  11 /* flush buffers for media; either for verifying media, or for
+                       handling a media change without closing the file
+                                              descriptor */
+#define OLDFDSETMAXERRS 12 /* set abortion and read_track threshold */
+#define OLDFDGETMAXERRS 14 /* get abortion and read_track threshold */
+#define OLDFDGETDRVTYP 16          /* get drive type: 5 1/4 or 3 1/2 */
+
+#define OLDFDSETDRVPRM 20 /* set drive parameters */
+#define OLDFDGETDRVPRM 21 /* get drive parameters */
+#define OLDFDGETDRVSTAT 22 /* get drive state */
+#define OLDFDPOLLDRVSTAT 23 /* get drive state */
+#define OLDFDRESET 24 /* reset FDC */
+
+#define OLDFD_RESET_IF_NEEDED 0
+#define OLDFD_RESET_IF_RAWCMD 1
+#define OLDFD_RESET_ALWAYS 2
+
+#define OLDFDGETFDCSTAT 25 /* get fdc state */
+#define OLDFDWERRORCLR  27 /* clear write error and badness information */
+#define OLDFDWERRORGET  28 /* get write error and badness information */
+
+#define OLDFDRAWCMD 30 /* send a raw command to the fdc */
+
+#define OLDFDTWADDLE 40 /* flicker motor-on bit before reading a sector */
+
+struct old_floppy_raw_cmd {
+  void *data;
+  long length;
+
+  unsigned char rate;
+  unsigned char flags;
+  unsigned char cmd_count;
+  unsigned char cmd[9];
+  unsigned char reply_count;
+  unsigned char reply[7];
+  int track;
+};
+
+struct old_floppy_fdc_state {
+	int spec1; /* spec1 value last used */
+	int spec2; /* spec2 value last used */
+	int dtr;
+	unsigned char version;  /* FDC version code */
+	unsigned char dor;
+	int address; /* io address */
+	unsigned int rawcmd:2;
+	unsigned int reset:1;
+	unsigned int need_configure:1;
+	unsigned int perp_mode:2;
+	unsigned int has_fifo:1;
+};
+
+
+#include <linux/fd.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+
+#include <linux/ioport.h>
+
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+#include "blk.h"
+
+
+/* Dma Memory related stuff */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order (int size)
+{
+	int order;
+
+	for (order = 0; order < NR_MEM_LISTS; ++order)
+		if (size <= (PAGE_SIZE << order))
+			return order;
+	return NR_MEM_LISTS;
+}
+
+static unsigned long dma_mem_alloc(int size)
+{
+	int order = __get_order(size);
+
+	if (order >= NR_MEM_LISTS)
+		return(0);
+	return __get_dma_pages(GFP_KERNEL,order);
+}
+
+/* End dma memory related stuff */
+
+static unsigned int fake_change = 0;
+static int initialising=1;
+
+#define FLOPPY0_TYPE 4
+#define FLOPPY1_TYPE 0
+
+#define N_FDC 2
+#define N_DRIVE 8
+
+static inline int TYPE(kdev_t x) {
+	return  (MINOR(x)>>2) & 0x1f;
+}
+static inline int DRIVE(kdev_t x) {
+	return (MINOR(x)&0x03) | ((MINOR(x)&0x80)>>5);
+}
+#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
+#define UNIT(x) ( (x) & 0x03 )		/* drive on fdc */
+#define FDC(x) ( ((x) & 0x04) >> 2 )  /* fdc of drive */
+#define REVDRIVE(fdc, unit) ( (unit) + ((fdc) << 2 ))
+				/* reverse mapping from unit and fdc to drive */
+#define DP (&drive_params[current_drive])
+#define DRS (&drive_state[current_drive])
+#define DRWE (&write_errors[current_drive])
+#define FDCS (&fdc_state[fdc])
+#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
+#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
+#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
+
+#define UDP (&drive_params[drive])
+#define UDRS (&drive_state[drive])
+#define UDRWE (&write_errors[drive])
+#define UFDCS (&fdc_state[FDC(drive)])
+#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
+#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
+#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
+
+#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
+
+#define DPRINT1(x,x1) printk(DEVICE_NAME "%d: " x,current_drive,(x1))
+
+#define DPRINT2(x,x1,x2) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2))
+
+#define DPRINT3(x,x1,x2,x3) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
+
+#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
+#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
+
+/* read/write */
+#define COMMAND raw_cmd->cmd[0]
+#define DR_SELECT raw_cmd->cmd[1]
+#define TRACK raw_cmd->cmd[2]
+#define HEAD raw_cmd->cmd[3]
+#define SECTOR raw_cmd->cmd[4]
+#define SIZECODE raw_cmd->cmd[5]
+#define SECT_PER_TRACK raw_cmd->cmd[6]
+#define GAP raw_cmd->cmd[7]
+#define SIZECODE2 raw_cmd->cmd[8]
+#define NR_RW 9
+
+/* format */
+#define F_SIZECODE raw_cmd->cmd[2]
+#define F_SECT_PER_TRACK raw_cmd->cmd[3]
+#define F_GAP raw_cmd->cmd[4]
+#define F_FILL raw_cmd->cmd[5]
+#define NR_F 6
+
+/*
+ * Maximum disk size (in kilobytes). This default is used whenever the
+ * current disk size is unknown.
+ * [Now it is rather a minimum]
+ */
+#define MAX_DISK_SIZE 2 /* 3984*/
+
+
+#define K_64	0x10000		/* 64kB */
+
+/*
+ * The DMA channel used by the floppy controller cannot access data at
+ * addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
+ */
+#define CROSS_64KB(a,s) (0)
+#undef LAST_DMA_ADDR
+#define LAST_DMA_ADDR	(0x1)
+
+/*
+ * globals used by 'result()'
+ */
+#define MAX_REPLIES 10
+static unsigned char reply_buffer[MAX_REPLIES];
+static int inr; /* size of reply buffer, when called from interrupt */
+#define ST0 (reply_buffer[0])
+#define ST1 (reply_buffer[1])
+#define ST2 (reply_buffer[2])
+#define ST3 (reply_buffer[0]) /* result of GETSTATUS */
+#define R_TRACK (reply_buffer[3])
+#define R_HEAD (reply_buffer[4])
+#define R_SECTOR (reply_buffer[5])
+#define R_SIZECODE (reply_buffer[6])
+
+#define SEL_DLY (2*HZ/100)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof( (x)[0] ))
+/*
+ * this struct defines the different floppy drive types.
+ */
+static struct {
+	struct floppy_drive_params params;
+	const char *name; /* name printed while booting */
+} default_drive_params[]= {
+/* NOTE: the time values in jiffies should be in msec!
+ CMOS drive type
+  |     Maximum data rate supported by drive type
+  |     |   Head load time, msec
+  |     |   |   Head unload time, msec (not used)
+  |     |   |   |     Step rate interval, usec
+  |     |   |   |     |       Time needed for spinup time (jiffies)
+  |     |   |   |     |       |      Timeout for spinning down (jiffies)
+  |     |   |   |     |       |      |   Spindown offset (where disk stops)
+  |     |   |   |     |       |      |   |     Select delay
+  |     |   |   |     |       |      |   |     |     RPS
+  |     |   |   |     |       |      |   |     |     |    Max number of tracks
+  |     |   |   |     |       |      |   |     |     |    |     Interrupt timeout
+  |     |   |   |     |       |      |   |     |     |    |     |   Max nonintlv. sectors
+  |     |   |   |     |       |      |   |     |     |    |     |   | -Max Errors- flags */
+{{0,  500, 16, 16, 8000,    1*HZ, 3*HZ,  0, SEL_DLY, 5,  80, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
+
+{{1,  300, 16, 16, 8000,    1*HZ, 3*HZ,  0, SEL_DLY, 5,  40, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2,  500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6,  83, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3,  250, 16, 16, 3000,    1*HZ, 3*HZ,  0, SEL_DLY, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
+#if 0
+{{4,  500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+#else
+{{4,  500, 16, 16, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
+      0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+#endif
+{{5, 1000, 15,  8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15,  8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
+/*    |  --autodetected formats---   |      |      |
+ *    read_track                     |      |    Name printed when booting
+ *                                   |     Native format
+ *                                 Frequency of disk change checks */
+};
+
+static struct floppy_drive_params drive_params[N_DRIVE];
+static struct floppy_drive_struct drive_state[N_DRIVE];
+static struct floppy_write_errors write_errors[N_DRIVE];
+static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
+
+/*
+ * This struct defines the different floppy types.
+ *
+ * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
+ * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
+ * tells if the disk is in Commodore 1581 format, which means side 0 sectors
+ * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
+ * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
+ * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
+ * side 0 is on physical side 0 (but with the misnamed sector IDs).
+ * 'stretch' should probably be renamed to something more general, like
+ * 'options'.  Other parameters should be self-explanatory (see also
+ * setfdprm(8)).
+ */
+/*
+             Size
+             |  Sectors per track
+             |  | Head
+             |  | |  Tracks
+             |  | |  | Stretch
+             |  | |  | |  Gap 1 size
+             |  | |  | |    |  Data rate, | 0x40 for perp
+	     |  | |  | |    |    |  Spec1 (stepping rate, head unload
+             |  | |  | |    |    |    |    /fmt gap (gap2) */
+static struct floppy_struct floppy_type[32] = {
+	{    0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL    },	/*  0 no testing    */
+	{  720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360"  }, /*  1 360KB PC      */
+	{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" },	/*  2 1.2MB AT      */
+	{  720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360"  },	/*  3 360KB SS 3.5" */
+	{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720"  },	/*  4 720KB 3.5"    */
+	{  720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360"  },	/*  5 360KB AT      */
+	{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720"  },	/*  6 720KB AT      */
+	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" },	/*  7 1.44MB 3.5"   */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" },	/*  8 2.88MB 3.5"   */
+	{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"CompaQ"},	/*  9 2.88MB 3.5"   */
+
+	{ 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25"  */
+	{ 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5"   */
+	{  820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410"  },	/* 12 410KB 5.25"   */
+	{ 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820"  },	/* 13 820KB 3.5"    */
+	{ 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" },	/* 14 1.48MB 5.25"  */
+	{ 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" },	/* 15 1.72MB 3.5"   */
+	{  840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420"  },	/* 16 420KB 5.25"   */
+	{ 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830"  },	/* 17 830KB 3.5"    */
+	{ 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" },	/* 18 1.49MB 5.25"  */
+	{ 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5"  */
+
+	{ 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880"  }, /* 20 880KB 5.25"   */
+	{ 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5"   */
+	{ 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5"   */
+	{ 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25"   */
+	{ 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5"   */
+	{ 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5"   */
+	{ 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5"   */
+	{ 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5"   */
+	{ 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5"   */
+
+	{ 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5"   */
+	{ 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800"  },	/* 30 800KB 3.5"    */
+	{ 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5"    */
+};
+
+#define	NUMBER(x)	(sizeof(x) / sizeof(*(x)))
+#define SECTSIZE ( _FD_SECTSIZE(*floppy))
+
+/* Auto-detection: Disk type used until the next media change occurs. */
+struct floppy_struct *current_type[N_DRIVE] = {
+	NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL
+};
+
+/*
+ * User-provided type information. current_type points to
+ * the respective entry of this array.
+ */
+struct floppy_struct user_params[N_DRIVE];
+
+static int floppy_sizes[256];
+static int floppy_blocksizes[256] = { 0, };
+
+/*
+ * The driver is trying to determine the correct media format
+ * while probing is set. rw_interrupt() clears it after a
+ * successful access.
+ */
+static int probing = 0;
+
+/* Synchronization of FDC access. */
+#define FD_COMMAND_DETECT -2
+#define FD_COMMAND_NONE -1
+#define FD_COMMAND_ERROR 2
+#define FD_COMMAND_OKAY 3
+
+static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
+static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
+#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
+#define CALL(x) if( (x) == -EINTR) return -EINTR
+#define ECALL(x) if ((ret = (x))) return ret;
+#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
+#define WAIT(x) _WAIT((x),interruptible)
+#define IWAIT(x) _WAIT((x),1)
+
+/* Errors during formatting are counted here. */
+static int format_errors;
+
+/* Format request descriptor. */
+static struct format_descr format_req;
+
+/*
+ * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
+ * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
+ * H is head unload time (1=16ms, 2=32ms, etc)
+ */
+
+/*
+ * Track buffer
+ * Because these are written to by the DMA controller, they must
+ * not contain a 64k byte boundary crossing, or data will be
+ * corrupted/lost. Alignment of these is enforced in boot/head.S.
+ * Note that you must not change the sizes below without updating head.S.
+ */
+char *floppy_track_buffer=NULL;
+int max_buffer_sectors=0;
+
+int *errors;
+typedef void (*done_f)(int);
+struct cont_t {
+	void (*interrupt)(void); /* this is called after the interrupt of the
+				  * main command */
+	void (*redo)(void); /* this is called to retry the operation */
+	void (*error)(void); /* this is called to tally an error */
+	done_f done; /* this is called to say if the operation has
+		      * succeeded/failed */
+} *cont=NULL;
+
+static void floppy_ready(void);
+static void floppy_start(void);
+static void process_fd_request(void);
+static void recalibrate_floppy(void);
+static void floppy_shutdown(void);
+
+static int floppy_grab_irq_and_dma(void);
+static void floppy_release_irq_and_dma(void);
+
+/*
+ * The "reset" variable should be tested whenever an interrupt is scheduled,
+ * after the commands have been sent. This is to ensure that the driver doesn't
+ * get wedged when the interrupt doesn't come because of a failed command.
+ * reset doesn't need to be tested before sending commands, because
+ * output_byte is automatically disabled when reset is set.
+ */
+#define CHECK_RESET { if ( FDCS->reset ){ reset_fdc(); return ; } }
+static void reset_fdc(void);
+
+/*
+ * These are global variables, as that's the easiest way to give
+ * information to interrupts. They are the data used for the current
+ * request.
+ */
+#define NO_TRACK -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
+
+/* */
+static int usage_count = 0;
+
+
+/* buffer related variables */
+static int buffer_track = -1;
+static int buffer_drive = -1;
+static int buffer_min = -1;
+static int buffer_max = -1;
+
+/* fdc related variables, should end up in a struct */
+static struct floppy_fdc_state fdc_state[N_FDC];
+static int fdc; /* current fdc */
+
+static struct floppy_struct * floppy = floppy_type;
+static unsigned char current_drive = 0;
+static long current_count_sectors = 0;
+static unsigned char sector_t; /* sector in track */
+
+#ifdef DEBUGT
+long unsigned debugtimer;
+#endif
+
+/*
+ * Floppy_selects1 is the list of DOR's to select a drive n
+ * Floppy_selects2 is the list of DOR's to select drive fd
+ * On initialisation, the floppy list is scanned, and the drives allocated
+ * in the order that they are found.  This is done by seeking the drive
+ * to a non-zero track, and then restoring it to track 0.  If an error occurs,
+ * then there is no floppy drive present.
+ */
+/*extern*/ int no_floppies;
+unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
+unsigned char floppy_selects2[]={ 0   , 0   , 0   , 0    };
+
+/*
+ * Debugging
+ * =========
+ */
+static inline void set_debugt(void)
+{
+#ifdef DEBUGT
+	debugtimer = jiffies;
+#endif
+}
+
+static inline void debugt(const char *message)
+{
+#ifdef DEBUGT
+  if ( DP->flags & DEBUGT )
+	printk("%s dtime=%lu\n", message, jiffies-debugtimer );
+#endif
+}
+
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
+					(timeout_fn) floppy_shutdown };
+
+static const char *timeout_message;
+
+#ifdef CONFIG_FLOPPY_SANITY
+static void is_alive(const char *message)
+{
+	/* this routine checks whether the floppy driver is "alive" */
+	if (fdc_busy && command_status < 2 && !fd_timeout.prev){
+		DPRINT1("timeout handler died: %s\n",message);
+	}
+}
+
+
+#define OLOGSIZE 20
+
+void (*lasthandler)(void) = NULL;
+int interruptjiffies=0;
+int resultjiffies=0;
+int resultsize=0;
+int lastredo=0;
+
+static struct output_log {
+	unsigned char data;
+	unsigned char status;
+	unsigned long jiffies;
+} output_log[OLOGSIZE];
+
+static int output_log_pos=0;
+#endif
+
+#define CURRENTD -1
+#define MAXTIMEOUT -2
+
+
+
+static void reschedule_timeout(int drive, const char *message, int marg)
+{
+	if (drive == CURRENTD )
+		drive = current_drive;
+	del_timer(&fd_timeout);
+	if (drive < 0 || drive > N_DRIVE) {
+		fd_timeout.expires = jiffies + 20*HZ;
+		drive = 0;
+	} else
+		fd_timeout.expires = jiffies + UDP->timeout;
+	add_timer(&fd_timeout);
+	if (UDP->flags & FD_DEBUG) {
+		DPRINT("reschedule timeout ");
+		printk(message, marg);
+		printk("\n");
+	}
+	timeout_message = message;
+}
+
+/*
+ * Bottom half floppy driver.
+ * ==========================
+ *
+ * This part of the file contains the code talking directly to the hardware,
+ * and also the main service loop (seek-configure-spinup-command)
+ */
+
+/*
+ * disk change.
+ * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
+ * and the last_checked date.
+ *
+ * last_checked is the date of the last check which showed 'no disk change'
+ * FD_DISK_CHANGE is set under two conditions:
+ * 1. The floppy has been changed after some i/o to that floppy already
+ *    took place.
+ * 2. No floppy disk is in the drive. This is done in order to ensure that
+ *    requests are quickly flushed in case there is no disk in the drive. It
+ *    follows that FD_DISK_CHANGE can only be cleared if there is a disk in
+ *    the drive.
+ *
+ * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
+ * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
+ *  each seek. If a disk is present, the disk change line should also be
+ *  cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
+ *  change line is set, this means either that no disk is in the drive, or
+ *  that it has been removed since the last seek.
+ *
+ * This means that we really have a third possibility too:
+ *  The floppy has been changed after the last seek.
+ */
+
+static int disk_change(int drive)
+{
+	int fdc=FDC(drive);
+#ifdef CONFIG_FLOPPY_SANITY
+	if(jiffies < UDP->select_delay + UDRS->select_date)
+		DPRINT("WARNING disk change called early\n");
+	if(! (FDCS->dor & (0x10 << UNIT(drive))) ||
+	   (FDCS->dor & 3) != UNIT(drive) ||
+	   fdc != FDC(drive)){
+	   	DPRINT("probing disk change on unselected drive\n");
+	   	DPRINT3("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
+	   		FDCS->dor);
+	}
+#endif
+
+#ifdef DCL_DEBUG
+	if (UDP->flags & FD_DEBUG){
+		DPRINT1("checking disk change line for drive %d\n",drive);
+		DPRINT1("jiffies=%ld\n", jiffies);
+		DPRINT1("disk change line=%x\n", inb_p(FD_DIR)&0x80);
+		DPRINT1("flags=%x\n",UDRS->flags);
+	}
+#endif
+	if (UDP->flags & FD_BROKEN_DCL)
+		return UTESTF(FD_DISK_CHANGED);
+	if( (inb_p(FD_DIR) ^ UDP->flags) & 0x80){
+		USETF(FD_VERIFY); /* verify write protection */
+		if(UDRS->maxblock){
+			/* mark it changed */
+			USETF(FD_DISK_CHANGED);
+
+			/* invalidate its geometry */
+			if (UDRS->keep_data >= 0) {
+				if ((UDP->flags & FTD_MSG) &&
+				    current_type[drive] != NULL)
+					DPRINT("Disk type is undefined after "
+					       "disk change\n");
+				current_type[drive] = NULL;
+				floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
+			}
+		}
+		/*USETF(FD_DISK_NEWCHANGE);*/
+		return 1;
+	} else {
+		UDRS->last_checked=jiffies;
+		UCLEARF(FD_DISK_NEWCHANGE);
+	}
+	return 0;
+}
+
+static inline int is_selected(int dor, int unit)
+{
+	return ( (dor  & (0x10 << unit)) && (dor &3) == unit);
+}
+
+static inline void arm_set_dor(int dor)
+{
+	if(dor & 0xf0)
+		outb_p((dor & 0x0c) | floppy_selects1[dor & 3], FD_DOR);
+	else
+		outb_p((dor & 0x0c), FD_DOR);
+}
+
+static int set_dor(int fdc, char mask, char data)
+{
+	register unsigned char drive, unit, newdor,olddor;
+
+	if (FDCS->address == -1)
+		return -1;
+
+	olddor = FDCS->dor;
+	newdor =  (olddor & mask) | data;
+	if ( newdor != olddor ){
+		unit = olddor & 0x3;
+		if(is_selected(olddor, unit) && !is_selected(newdor, unit)){
+			drive = REVDRIVE(fdc,unit);
+#ifdef DCL_DEBUG
+			if (UDP->flags & FD_DEBUG){
+				DPRINT("calling disk change from set_dor\n");
+			}
+#endif
+			disk_change(drive);
+		}
+		FDCS->dor = newdor;
+		arm_set_dor(newdor);
+
+		unit = newdor & 0x3;
+		if(!is_selected(olddor, unit) && is_selected(newdor, unit)){
+			drive = REVDRIVE(fdc,unit);
+			UDRS->select_date = jiffies;
+		}
+	}
+	if ( newdor & 0xf0 )
+		floppy_grab_irq_and_dma();
+	if( olddor & 0xf0 )
+		floppy_release_irq_and_dma();
+	return olddor;
+}
+
+static void twaddle(void)
+{
+	if (DP->select_delay)
+		return;
+	arm_set_dor(FDCS->dor & ~(0x10<<UNIT(current_drive)));
+	arm_set_dor(FDCS->dor);
+	DRS->select_date = jiffies;
+}
+
+/* reset all driver information about the current fdc. This is needed after
+ * a reset, and after a raw command. */
+static void reset_fdc_info(int mode)
+{
+	int drive;
+
+	FDCS->spec1 = FDCS->spec2 = -1;
+	FDCS->need_configure = 1;
+	FDCS->perp_mode = 1;
+	FDCS->rawcmd = 0;
+	for ( drive = 0; drive < N_DRIVE; drive++)
+		if (FDC(drive) == fdc &&
+		    ( mode || UDRS->track != NEED_1_RECAL))
+			UDRS->track = NEED_2_RECAL;
+}
+
+/* selects the fdc and drive, and enables the fdc's input/dma. */
+static void set_fdc(int drive)
+{
+	if ( drive >= 0 && drive < N_DRIVE){
+		fdc = FDC(drive);
+		current_drive = drive;
+	}
+	set_dor(fdc,~0,8);
+	set_dor(1-fdc, ~8, 0);
+	if ( FDCS->rawcmd == 2 )
+		reset_fdc_info(1);
+	if( inb_p(FD_STATUS) != STATUS_READY )
+		FDCS->reset = 1;
+}
+
+/* locks the driver */
+static int lock_fdc(int drive, int interruptible)
+{
+	if(!usage_count){
+		printk("trying to lock fdc while usage count=0\n");
+		return -1;
+	}
+	floppy_grab_irq_and_dma();
+	cli();
+	while (fdc_busy && NO_SIGNAL)
+		interruptible_sleep_on(&fdc_wait);
+	if(fdc_busy){
+		sti();
+		return -EINTR;
+	}
+	fdc_busy = 1;
+	sti();
+	command_status = FD_COMMAND_NONE;
+	set_fdc(drive);
+	reschedule_timeout(drive, "lock fdc", 0);
+	return 0;
+}
+
+#define LOCK_FDC(drive,interruptible) \
+if(lock_fdc(drive,interruptible)) return -EINTR;
+
+
+/* unlocks the driver */
+static inline void unlock_fdc(void)
+{
+	raw_cmd = NULL;
+	if (!fdc_busy)
+		DPRINT("FDC access conflict!\n");
+
+	if ( DEVICE_INTR )
+		DPRINT1("device interrupt still active at FDC release: %p!\n",
+			DEVICE_INTR);
+	command_status = FD_COMMAND_NONE;
+	del_timer(&fd_timeout);
+	cont = NULL;
+	fdc_busy = 0;
+	floppy_release_irq_and_dma();
+	wake_up(&fdc_wait);
+}
+
+/* switches the motor off after a given timeout */
+static void motor_off_callback(unsigned long nr)
+{
+	unsigned char mask = ~(0x10 << UNIT(nr));
+
+	set_dor( FDC(nr), mask, 0 );
+}
+
+static struct timer_list motor_off_timer[N_DRIVE] = {
+	{ NULL, NULL, 0, 0, motor_off_callback },
+	{ NULL, NULL, 0, 1, motor_off_callback },
+	{ NULL, NULL, 0, 2, motor_off_callback },
+	{ NULL, NULL, 0, 3, motor_off_callback },
+	{ NULL, NULL, 0, 4, motor_off_callback },
+	{ NULL, NULL, 0, 5, motor_off_callback },
+	{ NULL, NULL, 0, 6, motor_off_callback },
+	{ NULL, NULL, 0, 7, motor_off_callback }
+};
+
+/* schedules motor off */
+static void floppy_off(unsigned int drive)
+{
+	unsigned long volatile delta;
+	register int fdc=FDC(drive);
+
+	if( !(FDCS->dor & ( 0x10 << UNIT(drive))))
+		return;
+
+	del_timer(motor_off_timer+drive);
+
+	/* make spindle stop in a position which minimizes spinup time
+	 * next time */
+	if ( UDP->rps ){
+		delta = jiffies - UDRS->first_read_date + HZ -
+			UDP->spindown_offset;
+		delta = (( delta * UDP->rps) % HZ ) / UDP->rps;
+		motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
+	}
+	add_timer(motor_off_timer+drive);
+}
+
+/*
+ * cycle through all N_DRIVE floppy drives, for disk change testing.
+ * stopping at current drive. This is done before any long operation, to
+ * be sure to have up to date disk change information.
+ */
+static void scandrives(void)
+{
+	int i, drive, saved_drive;
+
+	if (DP->select_delay)
+		return;
+
+	saved_drive = current_drive;
+	for(i=0; i< N_DRIVE; i++){
+		drive = (saved_drive + i + 1 ) % N_DRIVE;
+		if ( UDRS->fd_ref == 0 || UDP->select_delay != 0)
+			continue; /* skip closed drives */
+		set_fdc(drive);
+		if(! (set_dor( fdc, ~3, UNIT(drive) | ( 0x10 << UNIT(drive))) &
+		      (0x10 << UNIT(drive))))
+			/* switch the motor off again, if it was off to
+			 * begin with */
+			set_dor( fdc, ~( 0x10 << UNIT(drive) ), 0 );
+	}
+	set_fdc(saved_drive);
+}
+
+static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
+
+/* this function makes sure that the disk stays in the drive during the
+ * transfer */
+static void fd_watchdog(void)
+{
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("calling disk change from watchdog\n");
+	}
+#endif
+
+	if ( disk_change(current_drive) ){
+		DPRINT("disk removed during i/o\n");
+		floppy_shutdown();
+	} else {
+		del_timer(&fd_timer);
+		fd_timer.function = (timeout_fn) fd_watchdog;
+		fd_timer.expires = jiffies + HZ / 10;
+		add_timer(&fd_timer);
+	}
+}
+
+static void main_command_interrupt(void)
+{
+	del_timer(&fd_timer);
+	cont->interrupt();
+}
+
+/* waits for a delay (spinup or select) to pass */
+static int wait_for_completion(int delay, timeout_fn function)
+{
+	if ( FDCS->reset ){
+		reset_fdc(); /* do the reset during sleep to win time
+			      * if we don't need to sleep, it's a good
+			      * occasion anyways */
+		return 1;
+	}
+
+	if ( jiffies < delay ){
+		del_timer(&fd_timer);
+		fd_timer.function = function;
+		fd_timer.expires = delay;
+		add_timer(&fd_timer);
+		return 1;
+	}
+	return 0;
+}
+
+static int hlt_disabled=0;
+static void floppy_disable_hlt(void)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	if(!hlt_disabled){
+		hlt_disabled=1;
+#ifdef HAVE_DISABLE_HLT
+		disable_hlt();
+#endif
+	}
+	restore_flags(flags);
+}
+
+static void floppy_enable_hlt(void)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	if(hlt_disabled){
+		hlt_disabled=0;
+#ifdef HAVE_DISABLE_HLT
+		enable_hlt();
+#endif
+	}
+	restore_flags(flags);
+}
+
+
+static void setup_DMA(void)
+{
+#ifdef CONFIG_FLOPPY_SANITY
+	if (raw_cmd->length == 0){
+		int i;
+
+		printk("zero dma transfer size:");
+		for(i=0; i< raw_cmd->cmd_count; i++)
+			printk("%x,", raw_cmd->cmd[i]);
+		printk("\n");
+		cont->done(0);
+		FDCS->reset = 1;
+		return;
+	}
+#if 0
+	/* disabled because of new buffer location for raw cmd */
+	if ((!CURRENT ||
+	     CURRENT->buffer != raw_cmd->kernel_data ||
+	     raw_cmd->length > 512 * CURRENT->nr_sectors) &&
+	    (raw_cmd->kernel_data < floppy_track_buffer ||
+	     raw_cmd->kernel_data + raw_cmd->length >
+	     floppy_track_buffer + 1024 * max_buffer_sectors)){
+		printk("bad address. start=%p lg=%lx tb=%p\n",
+		       raw_cmd->kernel_data, raw_cmd->length,
+		       floppy_track_buffer);
+		if ( CURRENT ){
+			printk("buffer=%p nr=%lx cnr=%lx\n",
+			       CURRENT->buffer, CURRENT->nr_sectors,
+			       CURRENT->current_nr_sectors);
+		}
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+	if ((long) raw_cmd->kernel_data % 512 ){
+		printk("non aligned address: %p\n", raw_cmd->kernel_data );
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+	if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
+		printk("DMA crossing 64-K boundary %p-%p\n",
+		       raw_cmd->kernel_data,
+		       raw_cmd->kernel_data + raw_cmd->length);
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+#endif
+#endif
+	cli();
+	disable_dma(FLOPPY_DMA);
+	clear_dma_ff(FLOPPY_DMA);
+	set_dma_mode(FLOPPY_DMA,
+		     (raw_cmd->flags & FD_RAW_READ)?
+		     DMA_MODE_READ : DMA_MODE_WRITE);
+	set_dma_addr(FLOPPY_DMA, virt_to_bus(raw_cmd->kernel_data));
+	set_dma_count(FLOPPY_DMA, raw_cmd->length);
+	enable_dma(FLOPPY_DMA);
+	sti();
+	floppy_disable_hlt();
+}
+
+/* sends a command byte to the fdc */
+static int output_byte(char byte)
+{
+	int counter;
+	unsigned char status=0;
+	unsigned char rstatus;
+
+	if (FDCS->reset)
+		return -1;
+	for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
+		rstatus = inb_p(FD_STATUS);
+		status = rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+		if (!(status & STATUS_READY))
+			continue;
+		if (status == STATUS_READY){
+			outb_p(byte,FD_DATA);
+
+#ifdef CONFIG_FLOPPY_SANITY
+			output_log[output_log_pos].data = byte;
+			output_log[output_log_pos].status = rstatus;
+			output_log[output_log_pos].jiffies = jiffies;
+			output_log_pos = (output_log_pos + 1) % OLOGSIZE;
+#endif
+			return 0;
+		} else
+			break;
+	}
+	FDCS->reset = 1;
+	if ( !initialising )
+		DPRINT2("Unable to send byte %x to FDC. Status=%x\n",
+			byte, status);
+	return -1;
+}
+#define LAST_OUT(x) if(output_byte(x)){ reset_fdc();return;}
+
+/* gets the response from the fdc */
+static int result(void)
+{
+	int i = 0, counter, status = 0;
+
+	if (FDCS->reset)
+		return -1;
+	for (counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
+		status = inb_p(FD_STATUS)&
+			(STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
+		if (!(status & STATUS_READY))
+			continue;
+		if (status == STATUS_READY){
+#ifdef CONFIG_FLOPPY_SANITY
+			resultjiffies = jiffies;
+			resultsize = i;
+#endif
+			return i;
+		}
+		if (status & STATUS_DMA )
+			break;
+		if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
+			if (i >= MAX_REPLIES) {
+				DPRINT("floppy_stat reply overrun\n");
+				break;
+			}
+			reply_buffer[i++] = inb_p(FD_DATA);
+		}
+	}
+	FDCS->reset = 1;
+	if ( !initialising )
+		DPRINT3("Getstatus times out (%x) on fdc %d [%d]\n",
+			status, fdc, i);
+	return -1;
+}
+
+/* Set perpendicular mode as required, based on data rate, if supported.
+ * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
+ */
+static inline void perpendicular_mode(void)
+{
+	unsigned char perp_mode;
+
+	if (!floppy)
+		return;
+	if (floppy->rate & 0x40){
+		switch(raw_cmd->rate){
+			case 0:
+				perp_mode=2;
+				break;
+			case 3:
+				perp_mode=3;
+				break;
+			default:
+				DPRINT("Invalid data rate for perpendicular mode!\n");
+				cont->done(0);
+				FDCS->reset = 1; /* convenient way to return to
+						  * redo without to much hassle (deep
+						  * stack et al. */
+				return;
+		}
+	} else
+		perp_mode = 0;
+
+	if ( FDCS->perp_mode == perp_mode )
+		return;
+	if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
+		output_byte(FD_PERPENDICULAR);
+		output_byte(perp_mode);
+		FDCS->perp_mode = perp_mode;
+	} else if (perp_mode) {
+		DPRINT("perpendicular mode not supported by this FDC.\n");
+	}
+} /* perpendicular_mode */
+
+#define NOMINAL_DTR 500
+
+/* Issue a "SPECIFY" command to set the step rate time, head unload time,
+ * head load time, and DMA disable flag to values needed by floppy.
+ *
+ * The value "dtr" is the data transfer rate in Kbps.  It is needed
+ * to account for the data rate-based scaling done by the 82072 and 82077
+ * FDC types.  This parameter is ignored for other types of FDCs (i.e.
+ * 8272a).
+ *
+ * Note that changing the data transfer rate has a (probably deleterious)
+ * effect on the parameters subject to scaling for 82072/82077 FDCs, so
+ * fdc_specify is called again after each data transfer rate
+ * change.
+ *
+ * srt: 1000 to 16000 in microseconds
+ * hut: 16 to 240 milliseconds
+ * hlt: 2 to 254 milliseconds
+ *
+ * These values are rounded up to the next highest available delay time.
+ */
+static void fdc_specify(void)
+{
+	unsigned char spec1, spec2;
+	int srt, hlt, hut;
+	unsigned long dtr = NOMINAL_DTR;
+	unsigned long scale_dtr = NOMINAL_DTR;
+	int hlt_max_code = 0x7f;
+	int hut_max_code = 0xf;
+
+	if (FDCS->need_configure && FDCS->has_fifo) {
+		if ( FDCS->reset )
+			return;
+		/* Turn on FIFO for 82077-class FDC (improves performance) */
+		/* TODO: lock this in via LOCK during initialization */
+		output_byte(FD_CONFIGURE);
+		output_byte(0);
+		output_byte(0x2A);	/* FIFO on, polling off, 10 byte threshold */
+		output_byte(0);		/* precompensation from track 0 upwards */
+		if ( FDCS->reset ){
+			FDCS->has_fifo=0;
+			return;
+		}
+		FDCS->need_configure = 0;
+		/*DPRINT("FIFO enabled\n");*/
+	}
+
+	switch (raw_cmd->rate & 0x03) {
+		case 3:
+			dtr = 1000;
+			break;
+		case 1:
+			dtr = 300;
+			break;
+		case 2:
+			dtr = 250;
+			break;
+	}
+
+	if (FDCS->version >= FDC_82072) {
+		scale_dtr = dtr;
+		hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
+		hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
+	}
+
+	/* Convert step rate from microseconds to milliseconds and 4 bits */
+	srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (srt > 0xf)
+		srt = 0xf;
+	else if (srt < 0)
+		srt = 0;
+
+	hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (hlt < 0x01)
+		hlt = 0x01;
+	else if (hlt > 0x7f)
+		hlt = hlt_max_code;
+
+	hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+	if (hut < 0x1)
+		hut = 0x1;
+	else if (hut > 0xf)
+		hut = hut_max_code;
+
+	spec1 = (srt << 4) | hut;
+	spec2 = (hlt << 1);
+
+	/* If these parameters did not change, just return with success */
+	if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
+		/* Go ahead and set spec1 and spec2 */
+		output_byte(FD_SPECIFY);
+		output_byte(FDCS->spec1 = spec1);
+		output_byte(FDCS->spec2 = spec2);
+	}
+} /* fdc_specify */
+
+/* Set the FDC's data transfer rate on behalf of the specified drive.
+ * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
+ * of the specify command (i.e. using the fdc_specify function).
+ */
+static int fdc_dtr(void)
+{
+	/* If data rate not already set to desired value, set it. */
+	if ( raw_cmd->rate == FDCS->dtr)
+		return 0;
+
+	/* Set dtr */
+	outb_p(raw_cmd->rate, FD_DCR);
+
+	/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
+	 * need a stabilization period of several milliseconds to be
+	 * enforced after data rate changes before R/W operations.
+	 * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
+	 */
+	FDCS->dtr = raw_cmd->rate;
+	return(wait_for_completion(jiffies+2*HZ/100,
+				   (timeout_fn) floppy_ready));
+} /* fdc_dtr */
+
+static void tell_sector(void)
+{
+	printk(": track %d, head %d, sector %d, size %d",
+	       R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
+} /* tell_sector */
+
+
+/*
+ * Ok, this error interpreting routine is called after a
+ * DMA read/write has succeeded
+ * or failed, so we check the results, and copy any buffers.
+ * hhb: Added better error reporting.
+ * ak: Made this into a separate routine.
+ */
+static int interpret_errors(void)
+{
+	char bad;
+int res = get_dma_residue(FLOPPY_DMA);
+if(res) {printk("\n-- DMA residue (%d)",res); tell_sector(); printk("\n");}
+	if (inr!=7) {
+		DPRINT("-- FDC reply error");
+		FDCS->reset = 1;
+		return 1;
+	}
+
+	/* check IC to find cause of interrupt */
+	switch (ST0 & ST0_INTR) {
+		case 0x40:	/* error occurred during command execution */
+			bad = 1;
+			if (ST1 & ST1_WP) {
+				DPRINT("Drive is write protected\n");
+				CLEARF(FD_DISK_WRITABLE);
+				cont->done(0);
+				bad = 2;
+			} else if (ST1 & ST1_ND) {
+				SETF(FD_NEED_TWADDLE);
+			} else if (ST1 & ST1_OR) {
+				if (DP->flags & FTD_MSG )
+					DPRINT("Over/Underrun - retrying\n");
+				bad = 0;
+			}else if(*errors >= DP->max_errors.reporting){
+				DPRINT("");
+				if (ST0 & ST0_ECE) {
+					printk("Recalibrate failed!");
+				} else if (ST2 & ST2_CRC) {
+					printk("data CRC error");
+					tell_sector();
+				} else if (ST1 & ST1_CRC) {
+					printk("CRC error");
+					tell_sector();
+				} else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
+					if (!probing) {
+						printk("sector not found");
+						tell_sector();
+					} else
+						printk("probe failed...");
+				} else if (ST2 & ST2_WC) {	/* seek error */
+					printk("wrong cylinder");
+				} else if (ST2 & ST2_BC) {	/* cylinder marked as bad */
+					printk("bad cylinder");
+				} else {
+					printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
+					tell_sector();
+				}
+				printk("\n");
+
+			}
+			if ( ST2 & ST2_WC || ST2 & ST2_BC)
+				/* wrong cylinder => recal */
+				DRS->track = NEED_2_RECAL;
+			return bad;
+		case 0x80: /* invalid command given */
+			DPRINT("Invalid FDC command given!\n");
+			cont->done(0);
+			return 2;
+		case 0xc0:
+			DPRINT("Abnormal termination caused by polling\n");
+			cont->error();
+			return 2;
+		default: /* (0) Normal command termination */
+			return 0;
+	}
+}
+
+/*
+ * This routine is called when everything should be correctly set up
+ * for the transfer (ie floppy motor is on, the correct floppy is
+ * selected, and the head is sitting on the right track).
+ */
+static void setup_rw_floppy(void)
+{
+	int i,ready_date,r, flags,dflags;
+	timeout_fn function;
+
+	flags = raw_cmd->flags;
+	if ( flags & ( FD_RAW_READ | FD_RAW_WRITE))
+		flags |= FD_RAW_INTR;
+
+	if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
+		ready_date = DRS->spinup_date + DP->spinup;
+		/* If spinup will take a long time, rerun scandrives
+		 * again just before spinup completion. Beware that
+		 * after scandrives, we must again wait for selection.
+		 */
+		if ( ready_date > jiffies + DP->select_delay){
+			ready_date -= DP->select_delay;
+			function = (timeout_fn) floppy_start;
+		} else
+			function = (timeout_fn) setup_rw_floppy;
+
+		/* wait until the floppy is spinning fast enough */
+		if (wait_for_completion(ready_date,function))
+			return;
+	}
+	dflags = DRS->flags;
+
+	if ( (flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
+		setup_DMA();
+
+	if ( flags & FD_RAW_INTR )
+		SET_INTR(main_command_interrupt);
+
+	r=0;
+	for(i=0; i< raw_cmd->cmd_count; i++)
+		r|=output_byte( raw_cmd->cmd[i] );
+
+#ifdef DEBUGT
+	debugt("rw_command: ");
+#endif
+	if ( r ){
+		reset_fdc();
+		return;
+	}
+
+	if ( ! ( flags & FD_RAW_INTR )){
+		inr = result();
+		cont->interrupt();
+	} else if ( flags & FD_RAW_NEED_DISK )
+		fd_watchdog();
+}
+
+static int blind_seek;
+
+/*
+ * This is the routine called after every seek (or recalibrate) interrupt
+ * from the floppy controller.
+ */
+static void seek_interrupt(void)
+{
+#ifdef DEBUGT
+	debugt("seek interrupt:");
+#endif
+	if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
+		DPRINT("seek failed\n");
+		DRS->track = NEED_2_RECAL;
+		cont->error();
+		cont->redo();
+		return;
+	}
+	if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
+#ifdef DCL_DEBUG
+		if (DP->flags & FD_DEBUG){
+			DPRINT("clearing NEWCHANGE flag because of effective seek\n");
+			DPRINT1("jiffies=%ld\n", jiffies);
+		}
+#endif
+		CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
+		DRS->select_date = jiffies;
+	}
+	DRS->track = ST1;
+	floppy_ready();
+}
+
+static void check_wp(void)
+{
+	if (TESTF(FD_VERIFY)) {
+		/* check write protection */
+		output_byte( FD_GETSTATUS );
+		output_byte( UNIT(current_drive) );
+		if ( result() != 1 ){
+			FDCS->reset = 1;
+			return;
+		}
+		CLEARF(FD_VERIFY);
+		CLEARF(FD_NEED_TWADDLE);
+#ifdef DCL_DEBUG
+		if (DP->flags & FD_DEBUG){
+			DPRINT("checking whether disk is write protected\n");
+			DPRINT1("wp=%x\n",ST3 & 0x40);
+		}
+#endif
+		if (!( ST3  & 0x40))
+			SETF(FD_DISK_WRITABLE);
+		else
+			CLEARF(FD_DISK_WRITABLE);
+	}
+}
+
+static void seek_floppy(void)
+{
+	int track;
+
+	blind_seek=0;
+
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("calling disk change from seek\n");
+	}
+#endif
+
+	if (!TESTF(FD_DISK_NEWCHANGE) &&
+	    disk_change(current_drive) &&
+	    (raw_cmd->flags & FD_RAW_NEED_DISK)){
+		/* the media changed flag should be cleared after the seek.
+		 * If it isn't, this means that there is really no disk in
+		 * the drive.
+		 */
+		SETF(FD_DISK_CHANGED);
+		cont->done(0);
+		cont->redo();
+		return;
+	}
+	if ( DRS->track <= NEED_1_RECAL ){
+		recalibrate_floppy();
+		return;
+	} else if (TESTF(FD_DISK_NEWCHANGE) &&
+		   (raw_cmd->flags & FD_RAW_NEED_DISK) &&
+		   (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
+		/* we seek to clear the media-changed condition. Does anybody
+		 * know a more elegant way, which works on all drives? */
+		if ( raw_cmd->track )
+			track = raw_cmd->track - 1;
+		else {
+			if(DP->flags & FD_SILENT_DCL_CLEAR){
+				set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
+				blind_seek = 1;
+				raw_cmd->flags |= FD_RAW_NEED_SEEK;
+			}
+			track = 1;
+		}
+	} else {
+		check_wp();
+		if (raw_cmd->track != DRS->track &&
+		    (raw_cmd->flags & FD_RAW_NEED_SEEK))
+			track = raw_cmd->track;
+		else {
+			setup_rw_floppy();
+			return;
+		}
+	}
+
+	SET_INTR(seek_interrupt);
+	output_byte(FD_SEEK);
+	output_byte(UNIT(current_drive));
+	LAST_OUT(track);
+#ifdef DEBUGT
+	debugt("seek command:");
+#endif
+}
+
+static void recal_interrupt(void)
+{
+#ifdef DEBUGT
+	debugt("recal interrupt:");
+#endif
+	if (inr !=2 )
+		FDCS->reset = 1;
+	else if (ST0 & ST0_ECE) {
+	       	switch(DRS->track){
+		       	case NEED_1_RECAL:
+#ifdef DEBUGT
+				debugt("recal interrupt need 1 recal:");
+#endif
+				/* after a second recalibrate, we still haven't
+				 * reached track 0. Probably no drive. Raise an
+				 * error, as failing immediately might upset
+				 * computers possessed by the Devil :-) */
+				cont->error();
+				cont->redo();
+				return;
+			case NEED_2_RECAL:
+#ifdef DEBUGT
+				debugt("recal interrupt need 2 recal:");
+#endif
+				/* If we already did a recalibrate,
+				 * and we are not at track 0, this
+				 * means we have moved. (The only way
+				 * not to move at recalibration is to
+				 * be already at track 0.) Clear the
+				 * new change flag */
+#ifdef DCL_DEBUG
+				if (DP->flags & FD_DEBUG){
+					DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
+				}
+#endif
+
+				CLEARF(FD_DISK_NEWCHANGE);
+				DRS->select_date = jiffies;
+				/* fall through */
+			default:
+#ifdef DEBUGT
+				debugt("recal interrupt default:");
+#endif
+				/* Recalibrate moves the head by at
+				 * most 80 steps. If after one
+				 * recalibrate we don't have reached
+				 * track 0, this might mean that we
+				 * started beyond track 80.  Try
+				 * again.  */
+				DRS->track = NEED_1_RECAL;
+				break;
+		}
+	} else
+		DRS->track = ST1;
+	floppy_ready();
+}
+
+/*
+ * Unexpected interrupt - Print as much debugging info as we can...
+ * All bets are off...
+ */
+static void unexpected_floppy_interrupt(void)
+{
+	int i;
+	if ( initialising )
+		return;
+	if(print_unex){
+		DPRINT("unexpected interrupt\n");
+		if ( inr >= 0 )
+			for(i=0; i<inr; i++)
+				printk("%d %x\n", i, reply_buffer[i] );
+	}
+	while(1){
+		output_byte(FD_SENSEI);
+		inr=result();
+		if ( inr != 2 )
+			break;
+		if(print_unex){
+			printk("sensei\n");
+			for(i=0; i<inr; i++)
+				printk("%d %x\n", i, reply_buffer[i] );
+		}
+	}
+	FDCS->reset = 1;
+}
+
+struct tq_struct floppy_tq =
+{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
+
+/* interrupt handler */
+static void floppy_interrupt(int irq, struct pt_regs *regs)
+{
+	void (*handler)(void) = DEVICE_INTR;
+
+	lasthandler = handler;
+	interruptjiffies = jiffies;
+
+	floppy_enable_hlt();
+	CLEAR_INTR;
+	if ( fdc >= N_FDC || FDCS->address == -1){
+		/* we don't even know which FDC is the culprit */
+		printk("DOR0=%x\n", fdc_state[0].dor);
+		printk("floppy interrupt on bizarre fdc %d\n",fdc);
+		printk("handler=%p\n", handler);
+		is_alive("bizarre fdc");
+		return;
+	}
+	inr = result();
+	if (!handler){
+		unexpected_floppy_interrupt();
+		is_alive("unexpected");
+		return;
+	}
+	if ( inr == 0 ){
+		do {
+			output_byte(FD_SENSEI);
+			inr = result();
+		} while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+	}
+	floppy_tq.routine = (void *)(void *) handler;
+	queue_task_irq(&floppy_tq, &tq_timer);
+	is_alive("normal interrupt end");
+}
+
+static void recalibrate_floppy(void)
+{
+#ifdef DEBUGT
+	debugt("recalibrate floppy:");
+#endif
+	SET_INTR(recal_interrupt);
+	output_byte(FD_RECALIBRATE);
+	LAST_OUT(UNIT(current_drive));
+}
+
+/*
+ * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
+ */
+static void reset_interrupt(void)
+{
+#ifdef DEBUGT
+	debugt("reset interrupt:");
+#endif
+	/* fdc_specify();	   reprogram fdc */
+	result();		/* get the status ready for set_fdc */
+	if ( FDCS->reset )
+		cont->error(); /* a reset just after a reset. BAD! */
+	cont->redo();
+}
+
+/*
+ * reset is done by pulling bit 2 of DOR low for a while (old FDC's),
+ * or by setting the self clearing bit 7 of STATUS (newer FDC's)
+ */
+static void reset_fdc(void)
+{
+	SET_INTR(reset_interrupt);
+	FDCS->reset = 0;
+	reset_fdc_info(0);
+	if ( FDCS->version >= FDC_82077 )
+		outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
+	else {
+		arm_set_dor(FDCS->dor & ~0x04);
+		udelay(FD_RESET_DELAY);
+		arm_set_dor(FDCS->dor);
+	}
+}
+
+static void empty(void)
+{
+}
+
+void show_floppy(void)
+{
+	int i;
+
+	printk("\n");
+	printk("floppy driver state\n");
+	printk("-------------------\n");
+	printk("now=%ld last interrupt=%d last called handler=%p\n",
+		jiffies, interruptjiffies, lasthandler);
+
+
+#ifdef CONFIG_FLOPPY_SANITY
+	printk("timeout_message=%s\n", timeout_message);
+	printk("last output bytes:\n");
+	for(i=0; i < OLOGSIZE; i++)
+		printk("%2x %2x %ld\n",
+			output_log[(i+output_log_pos) % OLOGSIZE].data,
+			output_log[(i+output_log_pos) % OLOGSIZE].status,
+			output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
+	printk("last result at %d\n", resultjiffies);
+	printk("last redo_fd_request at %d\n", lastredo);
+	for(i=0; i<resultsize; i++){
+		printk("%2x ", reply_buffer[i]);
+	}
+	printk("\n");
+#endif
+
+#if 0
+	for(i=0; i<N_FDC; i++){
+		if(FDCS->address != -1){
+			printk("dor %d = %x\n", i, fdc_state[i].dor );
+			outb_p(fdc_state[i].address+2, fdc_state[i].dor);
+			udelay(1000); /* maybe we'll catch an interrupt... */
+		}
+	}
+#endif
+	printk("status=%x\n", inb_p(FD_STATUS));
+	printk("fdc_busy=%d\n", fdc_busy);
+	if( DEVICE_INTR)
+		printk("DEVICE_INTR=%p\n", DEVICE_INTR);
+	if(floppy_tq.sync)
+		printk("floppy_tq.routine=%p\n", floppy_tq.routine);
+	if(fd_timer.prev)
+		printk("fd_timer.function=%p\n", fd_timer.function);
+	if(fd_timeout.prev){
+		printk("timer_table=%p\n",fd_timeout.function);
+		printk("expires=%ld\n",fd_timeout.expires-jiffies);
+		printk("now=%ld\n",jiffies);
+	}
+	printk("cont=%p\n", cont);
+	printk("CURRENT=%p\n", CURRENT);
+	printk("command_status=%d\n", command_status);
+	printk("\n");
+}
+
+static void floppy_shutdown(void)
+{
+	if (!initialising)
+		show_floppy();
+	CLEAR_INTR;
+	floppy_tq.routine = (void *)(void *) empty;
+	del_timer( &fd_timer);
+	sti();
+
+	floppy_enable_hlt();
+	disable_dma(FLOPPY_DMA);
+	/* avoid dma going to a random drive after shutdown */
+	
+	if(!initialising)
+		DPRINT("floppy timeout\n");
+	FDCS->reset = 1;
+	if (cont){
+		cont->done(0);
+		cont->redo(); /* this will recall reset when needed */
+	} else {
+		printk("no cont in shutdown!\n");
+		process_fd_request();
+	}
+	is_alive("floppy shutdown");
+}
+/*typedef void (*timeout_fn)(unsigned long);*/
+
+/* start motor, check media-changed condition and write protection */
+static int start_motor( void (*function)(void)  )
+{
+	int mask, data;
+
+	mask = 0xfc;
+	data = UNIT(current_drive);
+	if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
+		if(!(FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
+			set_debugt();
+			/* no read since this drive is running */
+			DRS->first_read_date = 0;
+			/* note motor start time if motor is not yet running */
+			DRS->spinup_date = jiffies;
+			data |= (0x10 << UNIT(current_drive));
+		}
+	} else
+		if (FDCS->dor & ( 0x10 << UNIT(current_drive) ) )
+			mask &= ~(0x10 << UNIT(current_drive));
+
+	/* starts motor and selects floppy */
+	del_timer(motor_off_timer + current_drive);
+	set_dor( fdc, mask, data);
+
+	/* wait_for_completion also schedules reset if needed. */
+	return(wait_for_completion(DRS->select_date+DP->select_delay,
+				   (timeout_fn) function));
+}
+
+static void floppy_ready(void)
+{
+	CHECK_RESET;
+	if(start_motor(floppy_ready)) return;
+	if(fdc_dtr()) return;
+
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("calling disk change from floppy_ready\n");
+	}
+#endif
+
+	if(!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
+	   disk_change(current_drive) &&
+	   !DP->select_delay)
+	   	twaddle(); /* this clears the dcl on certain drive/controller
+	   		    * combinations */
+
+	if ( raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
+		perpendicular_mode();
+		fdc_specify(); /* must be done here because of hut, hlt ... */
+		seek_floppy();
+	} else
+		setup_rw_floppy();
+}
+
+static void floppy_start(void)
+{
+	reschedule_timeout(CURRENTD, "floppy start", 0);
+
+	scandrives();
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("setting NEWCHANGE in floppy_start\n");
+	}
+#endif
+	SETF(FD_DISK_NEWCHANGE);
+	floppy_ready();
+}
+
+/*
+ * ========================================================================
+ * here ends the bottom half. Exported routines are:
+ * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
+ * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
+ * Initialisation also uses output_byte, result, set_dor, floppy_interrupt
+ * and set_dor.
+ * ========================================================================
+ */
+/*
+ * General purpose continuations.
+ * ==============================
+ */
+
+static void do_wakeup(void)
+{
+	reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
+	cont = 0;
+	command_status += 2;
+	wake_up(&command_done);
+}
+
+static struct cont_t wakeup_cont={
+	empty,
+	do_wakeup,
+	empty,
+	(done_f)empty
+};
+
+static int wait_til_done( void (*handler)(void ), int interruptible )
+{
+	int ret;
+
+	floppy_tq.routine = (void *)(void *) handler;
+	queue_task(&floppy_tq, &tq_timer);
+
+	cli();
+	while(command_status < 2 && NO_SIGNAL){
+		is_alive("wait_til_done");
+		if (interruptible)
+			interruptible_sleep_on(&command_done);
+		else
+			sleep_on(&command_done);
+	}
+	if(command_status < 2){
+		floppy_shutdown();
+		sti();
+		process_fd_request();
+		return -EINTR;
+	}
+	sti();
+
+	if ( FDCS->reset )
+		command_status = FD_COMMAND_ERROR;
+	if ( command_status == FD_COMMAND_OKAY )
+		ret=0;
+	else
+		ret=-EIO;
+	command_status = FD_COMMAND_NONE;
+	return ret;
+}
+
+static void generic_done(int result)
+{
+	command_status = result;
+	cont = &wakeup_cont;
+}
+
+static void generic_success(void)
+{
+	cont->done(1);
+}
+
+static void generic_failure(void)
+{
+	cont->done(0);
+}
+
+static void success_and_wakeup(void)
+{
+	generic_success();
+	cont->redo();
+}
+
+
+/*
+ * formatting and rw support.
+ * ==========================
+ */
+
+static int next_valid_format(void)
+{
+	int probed_format;
+
+	probed_format = DRS->probed_format;
+	while(1){
+		if ( probed_format >= 8 ||
+		    ! DP->autodetect[probed_format] ){
+			DRS->probed_format = 0;
+			return 1;
+		}
+		if ( floppy_type[DP->autodetect[probed_format]].sect ){
+			DRS->probed_format = probed_format;
+			return 0;
+		}
+		probed_format++;
+	}
+}
+
+static void bad_flp_intr(void)
+{
+	if ( probing ){
+		DRS->probed_format++;
+		if ( !next_valid_format())
+			return;
+	}
+	(*errors)++;
+	if (*errors > DRWE->badness)
+	        DRWE->badness = *errors;
+	if (*errors > DP->max_errors.abort)
+		cont->done(0);
+	if (*errors > DP->max_errors.reset)
+		FDCS->reset = 1;
+	else if (*errors > DP->max_errors.recal)
+		DRS->track = NEED_2_RECAL;
+}
+
+static void set_floppy(kdev_t device)
+{
+	if (TYPE(device))
+		floppy = TYPE(device) + floppy_type;
+	else
+		floppy = current_type[ DRIVE(device) ];
+}
+
+/*
+ * formatting and support.
+ * =======================
+ */
+static void format_interrupt(void)
+{
+	switch (interpret_errors()){
+		case 1:
+			cont->error();
+		case 2:
+			break;
+		case 0:
+			cont->done(1);
+	}
+	cont->redo();
+}
+
+#define CODE2SIZE (ssize = ( ( 1 << SIZECODE ) + 3 ) >> 2)
+#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80 ) >>1))
+#define CT(x) ( (x) | 0x40 )
+static void setup_format_params(int track)
+{
+	struct fparm {
+		unsigned char track,head,sect,size;
+	} *here = (struct fparm *)floppy_track_buffer;
+	int il,n;
+	int count,head_shift,track_shift;
+
+	raw_cmd = &default_raw_cmd;
+	raw_cmd->track = track;
+	
+	raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
+		/*FD_RAW_NEED_DISK |*/ FD_RAW_NEED_SEEK;
+	raw_cmd->rate = floppy->rate & 0x3;
+	raw_cmd->cmd_count = NR_F;
+	COMMAND = FM_MODE(floppy,FD_FORMAT);
+	DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,format_req.head);
+	F_SIZECODE = FD_SIZECODE(floppy);
+	F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
+	F_GAP = floppy->fmt_gap;
+	F_FILL = FD_FILL_BYTE;
+
+	raw_cmd->kernel_data = floppy_track_buffer;
+	raw_cmd->length = 4 * F_SECT_PER_TRACK;
+
+	/* allow for about 30ms for data transport per track */
+	head_shift  = (F_SECT_PER_TRACK + 5) / 6;
+
+	/* a ``cylinder'' is two tracks plus a little stepping time */
+	track_shift = 2 * head_shift + 3;
+
+	/* position of logical sector 1 on this track */
+	n = (track_shift * format_req.track + head_shift * format_req.head )
+		% F_SECT_PER_TRACK;
+
+	/* determine interleave */
+	il = 1;
+	if (floppy->sect > DP->interleave_sect && F_SIZECODE == 2)
+		il++;
+
+	/* initialize field */
+	for (count = 0; count < F_SECT_PER_TRACK; ++count) {
+		here[count].track = format_req.track;
+		here[count].head = format_req.head;
+		here[count].sect = 0;
+		here[count].size = F_SIZECODE;
+	}
+	/* place logical sectors */
+	for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
+		here[n].sect = count;
+		n = (n+il) % F_SECT_PER_TRACK;
+		if (here[n].sect) { /* sector busy, find next free sector */
+			++n;
+			if (n>= F_SECT_PER_TRACK) {
+				n-=F_SECT_PER_TRACK;
+				while (here[n].sect) ++n;
+			}
+		}
+	}
+}
+
+static void redo_format(void)
+{
+	buffer_track = -1;
+	setup_format_params(format_req.track << STRETCH(floppy));
+	floppy_start();
+#ifdef DEBUGT
+	debugt("queue format request");
+#endif
+}
+
+static struct cont_t format_cont={
+	format_interrupt,
+	redo_format,
+	bad_flp_intr,
+	generic_done };
+
+static int do_format(kdev_t device, struct format_descr *tmp_format_req)
+{
+	int ret;
+	int drive=DRIVE(device);
+
+	LOCK_FDC(drive,1);
+	set_floppy(device);
+	if (!floppy ||
+	    floppy->track > DP->tracks ||
+	    tmp_format_req->track >= floppy->track ||
+	    tmp_format_req->head >= floppy->head ||
+	    (floppy->sect << 2) % (1 << FD_SIZECODE(floppy)) ||
+	    !floppy->fmt_gap) {
+		process_fd_request();
+		return -EINVAL;
+	}
+	format_req = *tmp_format_req;
+	format_errors = 0;
+	cont = &format_cont;
+	errors = &format_errors;
+	IWAIT(redo_format);
+	process_fd_request();
+	return ret;
+}
+
+/*
+ * Buffer read/write and support
+ * =============================
+ */
+
+/* new request_done. Can handle physical sectors which are smaller than a
+ * logical buffer */
+static void request_done(int uptodate)
+{
+	int block;
+
+	probing = 0;
+	reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
+
+	if (!CURRENT){
+		DPRINT("request list destroyed in floppy request done\n");
+		return;
+	}
+	if (uptodate){
+		/* maintain values for invalidation on geometry
+		 * change */
+		block = current_count_sectors + CURRENT->sector;
+		if (block > DRS->maxblock)
+			DRS->maxblock=block;
+		if ( block > floppy->sect)
+			DRS->maxtrack = 1;
+
+		/* unlock chained buffers */
+		while (current_count_sectors && CURRENT &&
+		       current_count_sectors >= CURRENT->current_nr_sectors ){
+			current_count_sectors -= CURRENT->current_nr_sectors;
+			CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+			CURRENT->sector += CURRENT->current_nr_sectors;
+			end_request(1);
+		}
+		if ( current_count_sectors && CURRENT){
+			/* "unlock" last subsector */
+			CURRENT->buffer += current_count_sectors <<9;
+			CURRENT->current_nr_sectors -= current_count_sectors;
+			CURRENT->nr_sectors -= current_count_sectors;
+			CURRENT->sector += current_count_sectors;
+			return;
+		}
+
+		if ( current_count_sectors && ! CURRENT )
+			DPRINT("request list destroyed in floppy request done\n");
+
+	} else {
+		if(CURRENT->cmd == WRITE) {
+			/* record write error information */
+			DRWE->write_errors++;
+			if(DRWE->write_errors == 1) {
+				DRWE->first_error_sector = CURRENT->sector;
+				DRWE->first_error_generation = DRS->generation;
+			}
+			DRWE->last_error_sector = CURRENT->sector;
+			DRWE->last_error_generation = DRS->generation;
+		}
+		end_request(0);
+	}
+}
+
+/* Interrupt handler evaluating the result of the r/w operation */
+static void rw_interrupt(void)
+{
+	int nr_sectors, ssize;
+
+	if ( ! DRS->first_read_date )
+		DRS->first_read_date = jiffies;
+
+	nr_sectors = 0;
+	CODE2SIZE;
+	nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) *
+		floppy->sect + ((R_SECTOR-SECTOR) <<  SIZECODE >> 2) -
+		(sector_t % floppy->sect) % ssize;
+
+#ifdef CONFIG_FLOPPY_SANITY
+	if ( nr_sectors > current_count_sectors + ssize -
+	     (current_count_sectors + sector_t) % ssize +
+	     sector_t % ssize){
+		DPRINT2("long rw: %x instead of %lx\n",
+			nr_sectors, current_count_sectors);
+		printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
+		printk("rh=%d h=%d\n", R_HEAD, HEAD);
+		printk("rt=%d t=%d\n", R_TRACK, TRACK);
+		printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
+		       sector_t, ssize);
+	}
+#endif
+	if ( nr_sectors < 0 )
+		nr_sectors = 0;
+	if ( nr_sectors < current_count_sectors )
+		current_count_sectors = nr_sectors;
+
+	switch (interpret_errors()){
+		case 2:
+			cont->redo();
+			return;
+		case 1:
+			if (  !current_count_sectors){
+				cont->error();
+				cont->redo();
+				return;
+			}
+			break;
+		case 0:
+			if (  !current_count_sectors){
+				cont->redo();
+				return;
+			}
+			current_type[current_drive] = floppy;
+			floppy_sizes[TOMINOR(current_drive) ]= floppy->size>>1;
+			break;
+	}
+
+	if (probing) {
+		if (DP->flags & FTD_MSG)
+			DPRINT2("Auto-detected floppy type %s in fd%d\n",
+				floppy->name,current_drive);
+		current_type[current_drive] = floppy;
+		floppy_sizes[TOMINOR(current_drive)] = floppy->size >> 1;
+		probing = 0;
+	}
+
+	if ( CT(COMMAND) != FD_READ ||
+	     raw_cmd->kernel_data == CURRENT->buffer ){
+		/* transfer directly from buffer */
+		cont->done(1);
+	} else if ( CT(COMMAND) == FD_READ){
+		buffer_track = raw_cmd->track;
+		buffer_drive = current_drive;
+		if ( nr_sectors + sector_t > buffer_max )
+			buffer_max = nr_sectors + sector_t;
+	}
+	cont->redo();
+}
+
+/* Compute maximal contiguous buffer size. */
+static int buffer_chain_size(void)
+{
+	struct buffer_head *bh;
+	int size;
+	char *base;
+
+	base = CURRENT->buffer;
+	size = CURRENT->current_nr_sectors << 9;
+	bh = CURRENT->bh;
+
+	if(bh){
+		bh = bh->b_reqnext;
+		while ( bh && bh->b_data == base + size ){
+			size += bh->b_size;
+			bh = bh->b_reqnext;
+		}
+	}
+	return size >> 9;
+}
+
+/* Compute the maximal transfer size */
+static int transfer_size(int ssize, int max_sector, int max_size)
+{
+	if ( max_sector > sector_t + max_size)
+		max_sector = sector_t + max_size;
+
+	/* alignment */
+	max_sector -= (max_sector % floppy->sect ) % ssize;
+
+	/* transfer size, beginning not aligned */
+	current_count_sectors = max_sector - sector_t ;
+
+	return max_sector;
+}
+
+/*
+ * Move data from/to the track buffer to/from the buffer cache.
+ */
+static void copy_buffer(int ssize, int max_sector, int max_sector_2)
+{
+	int remaining; /* number of transferred 512-byte sectors */
+	struct buffer_head *bh;
+	char *buffer, *dma_buffer;
+	int size;
+
+	if ( max_sector > max_sector_2 )
+		max_sector = max_sector_2;
+
+	max_sector = transfer_size(ssize, max_sector, CURRENT->nr_sectors);
+
+	if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
+	    buffer_max > sector_t + CURRENT->nr_sectors){
+		current_count_sectors = buffer_max - sector_t;
+		if ( current_count_sectors > CURRENT->nr_sectors )
+			current_count_sectors = CURRENT->nr_sectors;
+	}
+	remaining = current_count_sectors << 9;
+#ifdef CONFIG_FLOPPY_SANITY
+	if ((remaining >> 9) > CURRENT->nr_sectors  &&
+	    CT(COMMAND) == FD_WRITE ){
+		DPRINT("in copy buffer\n");
+		printk("current_count_sectors=%ld\n", current_count_sectors);
+		printk("remaining=%d\n", remaining >> 9);
+		printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors);
+		printk("CURRENT->current_nr_sectors=%ld\n",
+		       CURRENT->current_nr_sectors);
+		printk("max_sector=%d\n", max_sector);
+		printk("ssize=%d\n", ssize);
+	}
+#endif
+
+	if ( max_sector > buffer_max )
+		buffer_max = max_sector;
+
+	dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
+
+	bh = CURRENT->bh;
+	size = CURRENT->current_nr_sectors << 9;
+	buffer = CURRENT->buffer;
+
+	while ( remaining > 0){
+		if ( size > remaining )
+			size = remaining;
+#ifdef CONFIG_FLOPPY_SANITY
+		if (dma_buffer + size >
+		    floppy_track_buffer + (max_buffer_sectors << 10) ||
+		    dma_buffer < floppy_track_buffer ){
+			DPRINT1("buffer overrun in copy buffer %d\n",
+				(int) ((floppy_track_buffer - dma_buffer) >>9));
+			printk("sector_t=%d buffer_min=%d\n",
+			       sector_t, buffer_min);
+			printk("current_count_sectors=%ld\n",
+			       current_count_sectors);
+			if ( CT(COMMAND) == FD_READ )
+				printk("read\n");
+			if ( CT(COMMAND) == FD_READ )
+				printk("write\n");
+			break;
+		}
+		if ( ((unsigned long)buffer) % 512 )
+			DPRINT1("%p buffer not aligned\n", buffer);
+#endif
+		if ( CT(COMMAND) == FD_READ )
+			memcpy( buffer, dma_buffer, size);
+		else
+			memcpy( dma_buffer, buffer, size);
+		remaining -= size;
+		if ( !remaining)
+			break;
+
+		dma_buffer += size;
+		bh = bh->b_reqnext;
+#ifdef CONFIG_FLOPPY_SANITY
+		if ( !bh){
+			DPRINT("bh=null in copy buffer after copy\n");
+			break;
+		}
+#endif
+		size = bh->b_size;
+		buffer = bh->b_data;
+	}
+#ifdef CONFIG_FLOPPY_SANITY
+	if ( remaining ){
+		if ( remaining > 0 )
+			max_sector -= remaining >> 9;
+		DPRINT1("weirdness: remaining %d\n", remaining>>9);
+	}
+#endif
+}
+
+/*
+ * Formulate a read/write request.
+ * this routine decides where to load the data (directly to buffer, or to
+ * tmp floppy area), how much data to load (the size of the buffer, the whole
+ * track, or a single sector)
+ * All floppy_track_buffer handling goes in here. If we ever add track buffer
+ * allocation on the fly, it should be done here. No other part should need
+ * modification.
+ */
+
+static int make_raw_rw_request(void)
+{
+	int aligned_sector_t;
+	int max_sector, max_size, tracksize, ssize;
+
+	set_fdc(DRIVE(CURRENT->rq_dev));
+
+	raw_cmd = &default_raw_cmd;
+	raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
+		FD_RAW_NEED_SEEK;
+	raw_cmd->cmd_count = NR_RW;
+	if (CURRENT->cmd == READ){
+		raw_cmd->flags |= FD_RAW_READ;
+		COMMAND = FM_MODE(floppy,FD_READ);
+	} else if (CURRENT->cmd == WRITE){
+		raw_cmd->flags |= FD_RAW_WRITE;
+		COMMAND = FM_MODE(floppy,FD_WRITE);
+	} else {
+		DPRINT("make_raw_rw_request: unknown command\n");
+		return 0;
+	}
+
+	max_sector = floppy->sect * floppy->head;
+	
+	TRACK = CURRENT->sector / max_sector;
+	sector_t = CURRENT->sector % max_sector;
+	if ( floppy->track && TRACK >= floppy->track )
+		return 0;
+	HEAD = sector_t / floppy->sect;
+
+	if (((floppy->stretch & FD_SWAPSIDES) || TESTF( FD_NEED_TWADDLE)) &&
+	    sector_t < floppy->sect )
+		max_sector = floppy->sect;
+
+	/* 2M disks have phantom sectors on the first track */
+	if ( (floppy->rate & FD_2M ) && (!TRACK) && (!HEAD)){
+		max_sector = 2 * floppy->sect / 3;
+		if (sector_t >= max_sector){
+			current_count_sectors =  (floppy->sect - sector_t);
+			if ( current_count_sectors > CURRENT->nr_sectors )
+				current_count_sectors = CURRENT->nr_sectors;
+			return 1;
+		}
+		SIZECODE = 2;
+	} else
+		SIZECODE = FD_SIZECODE(floppy);
+	raw_cmd->rate = floppy->rate & 3;
+	if ((floppy->rate & FD_2M) &&
+	    (TRACK || HEAD ) &&
+	    raw_cmd->rate == 2)
+		raw_cmd->rate = 1;
+
+	if ( SIZECODE )
+		SIZECODE2 = 0xff;
+	else
+		SIZECODE2 = 0x80;
+	raw_cmd->track = TRACK << STRETCH(floppy);
+	DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,HEAD);
+	GAP = floppy->gap;
+	CODE2SIZE;
+	SECT_PER_TRACK = floppy->sect << 2 >> SIZECODE;
+	SECTOR = ((sector_t % floppy->sect) << 2 >> SIZECODE) + 1;
+	tracksize = floppy->sect - floppy->sect % ssize;
+	if ( tracksize < floppy->sect ){
+		SECT_PER_TRACK ++;
+		if (  tracksize <= sector_t % floppy->sect)
+			SECTOR--;
+		while ( tracksize <= sector_t % floppy->sect){
+			while( tracksize + ssize > floppy->sect ){
+				SIZECODE--;
+				ssize >>= 1;
+			}
+			SECTOR++; SECT_PER_TRACK ++;
+			tracksize += ssize;
+		}
+		max_sector = HEAD * floppy->sect + tracksize;
+	} else if ( !TRACK && !HEAD && !( floppy->rate & FD_2M ) && probing)
+		max_sector = floppy->sect;
+
+	aligned_sector_t = sector_t - ( sector_t % floppy->sect ) % ssize;
+	max_size = CURRENT->nr_sectors;
+	if ((raw_cmd->track == buffer_track) && (current_drive == buffer_drive) &&
+	    (sector_t >= buffer_min) && (sector_t < buffer_max)) {
+		/* data already in track buffer */
+		if (CT(COMMAND) == FD_READ) {
+			copy_buffer(1, max_sector, buffer_max);
+			return 1;
+		}
+	} else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
+		if (CT(COMMAND) == FD_WRITE){
+			if(sector_t + CURRENT->nr_sectors > ssize &&
+			   sector_t + CURRENT->nr_sectors < ssize + ssize)
+				max_size = ssize + ssize;
+			else
+				max_size = ssize;
+		}
+		raw_cmd->flags &= ~FD_RAW_WRITE;
+		raw_cmd->flags |= FD_RAW_READ;
+		COMMAND = FM_MODE(floppy,FD_READ);
+	} else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS ) {
+		int direct, indirect;
+
+		indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
+			sector_t;
+
+		max_size = buffer_chain_size();
+#if 0
+		if ( max_size > ( MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9)
+			max_size=(MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer))>>9;
+		/* 64 kb boundaries */
+		if (CROSS_64KB(CURRENT->buffer, max_size << 9))
+			max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9;
+#endif
+		direct = transfer_size(ssize,max_sector,max_size) - sector_t;
+		/*
+		 * We try to read tracks, but if we get too many errors, we
+		 * go back to reading just one sector at a time.
+		 *
+		 * This means we should be able to read a sector even if there
+		 * are other bad sectors on this track.
+		 */
+		if (!direct ||
+		    (indirect * 2 > direct * 3 &&
+		     *errors < DP->max_errors.read_track &&
+		     /*!TESTF( FD_NEED_TWADDLE) &&*/
+		     ((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
+			max_size = CURRENT->nr_sectors;
+		} else {
+			raw_cmd->kernel_data = CURRENT->buffer;
+			raw_cmd->length = current_count_sectors << 9;
+			if (raw_cmd->length == 0){
+				DPRINT("zero dma transfer attempted from make_raw_request\n");
+				DPRINT3("indirect=%d direct=%d sector_t=%d",
+					indirect, direct, sector_t);
+				return 0;
+			}
+			return 2;
+		}
+	}
+
+	if ( CT(COMMAND) == FD_READ )
+		max_size = max_sector; /* unbounded */
+
+	/* claim buffer track if needed */
+	if (buffer_track != raw_cmd->track ||  /* bad track */
+	    buffer_drive !=current_drive || /* bad drive */
+	    sector_t > buffer_max ||
+	    sector_t < buffer_min ||
+	    ((CT(COMMAND) == FD_READ ||
+	      (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize ))&&
+	     max_sector > 2 * max_buffer_sectors + buffer_min &&
+	     max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
+	    /* not enough space */ ){
+		buffer_track = -1;
+		buffer_drive = current_drive;
+		buffer_max = buffer_min = aligned_sector_t;
+	}
+	raw_cmd->kernel_data = floppy_track_buffer +
+		((aligned_sector_t-buffer_min )<<9);
+
+	if ( CT(COMMAND) == FD_WRITE ){
+		/* copy write buffer to track buffer.
+		 * if we get here, we know that the write
+		 * is either aligned or the data already in the buffer
+		 * (buffer will be overwritten) */
+#ifdef CONFIG_FLOPPY_SANITY
+		if (sector_t != aligned_sector_t && buffer_track == -1 )
+			DPRINT("internal error offset !=0 on write\n");
+#endif
+		buffer_track = raw_cmd->track;
+		buffer_drive = current_drive;
+		copy_buffer(ssize, max_sector, 2*max_buffer_sectors+buffer_min);
+	} else
+		transfer_size(ssize, max_sector,
+			      2*max_buffer_sectors+buffer_min-aligned_sector_t);
+
+	/* round up current_count_sectors to get dma xfer size */
+	raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
+	raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
+	raw_cmd->length <<= 9;
+#ifdef CONFIG_FLOPPY_SANITY
+	if ((raw_cmd->length < current_count_sectors << 9) ||
+	    (raw_cmd->kernel_data != CURRENT->buffer &&
+	     CT(COMMAND) == FD_WRITE &&
+	     (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
+	      aligned_sector_t < buffer_min )) ||
+	    raw_cmd->length % ( 128 << SIZECODE ) ||
+	    raw_cmd->length <= 0 || current_count_sectors <= 0){
+		DPRINT2("fractionary current count b=%lx s=%lx\n",
+			raw_cmd->length, current_count_sectors);
+		if ( raw_cmd->kernel_data != CURRENT->buffer )
+			printk("addr=%d, length=%ld\n",
+			       (int) ((raw_cmd->kernel_data -
+				       floppy_track_buffer ) >> 9),
+			       current_count_sectors);
+		printk("st=%d ast=%d mse=%d msi=%d\n",
+		       sector_t, aligned_sector_t, max_sector, max_size);
+		printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE);
+		printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
+		       COMMAND, SECTOR, HEAD, TRACK);
+		printk("buffer drive=%d\n", buffer_drive);
+		printk("buffer track=%d\n", buffer_track);
+		printk("buffer_min=%d\n", buffer_min );
+		printk("buffer_max=%d\n", buffer_max );
+		return 0;
+	}
+
+	if (raw_cmd->kernel_data != CURRENT->buffer ){
+		if (raw_cmd->kernel_data < floppy_track_buffer ||
+		    current_count_sectors < 0 ||
+		    raw_cmd->length < 0 ||
+		    raw_cmd->kernel_data + raw_cmd->length >
+		    floppy_track_buffer + (max_buffer_sectors  << 10)){
+			DPRINT("buffer overrun in schedule dma\n");
+			printk("sector_t=%d buffer_min=%d current_count=%ld\n",
+			       sector_t, buffer_min,
+			       raw_cmd->length >> 9 );
+			printk("current_count_sectors=%ld\n",
+			       current_count_sectors);
+			if ( CT(COMMAND) == FD_READ )
+				printk("read\n");
+			if ( CT(COMMAND) == FD_READ )
+				printk("write\n");
+			return 0;
+		}
+	} else if (raw_cmd->length > CURRENT->nr_sectors << 9 ||
+		   current_count_sectors > CURRENT->nr_sectors){
+		DPRINT("buffer overrun in direct transfer\n");
+		return 0;
+	} else if ( raw_cmd->length < current_count_sectors << 9 ){
+		DPRINT("more sectors than bytes\n");
+		printk("bytes=%ld\n", raw_cmd->length >> 9 );
+		printk("sectors=%ld\n", current_count_sectors);
+	}
+	if (raw_cmd->length == 0){
+		DPRINT("zero dma transfer attempted from make_raw_request\n");
+		return 0;
+	}
+#endif
+	return 2;
+}
+
+static void redo_fd_request(void)
+{
+#define REPEAT {request_done(0); continue; }
+	kdev_t device;
+	int tmp;
+
+	lastredo = jiffies;
+	if (current_drive < N_DRIVE)
+		floppy_off(current_drive);
+
+	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+		DPRINT("current not active!\n");
+		return;
+	}
+
+	while(1){
+		if (!CURRENT) {
+			CLEAR_INTR;
+			unlock_fdc();
+			return;
+		}
+		if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+			panic(DEVICE_NAME ": request list destroyed");
+		if (CURRENT->bh && !CURRENT->bh->b_lock)
+			panic(DEVICE_NAME ": block not locked");
+
+		device = CURRENT->rq_dev;
+		set_fdc( DRIVE(device));
+		reschedule_timeout(CURRENTD, "redo fd request", 0);
+
+		set_floppy(device);
+		raw_cmd = & default_raw_cmd;
+		raw_cmd->flags = 0;
+		if(start_motor(redo_fd_request)) return;
+		if(test_bit(current_drive, &fake_change) ||
+		   TESTF(FD_DISK_CHANGED)){
+			DPRINT("disk absent or changed during operation\n");
+			REPEAT;
+		}
+		if (!floppy) { /* Autodetection */
+			if (!probing){
+				DRS->probed_format = 0;
+				if ( next_valid_format() ){
+					DPRINT("no autodetectable formats\n");
+					floppy = NULL;
+					REPEAT;
+				}
+			}
+			probing = 1;
+			floppy = floppy_type+DP->autodetect[DRS->probed_format];
+		} else
+			probing = 0;
+		errors = & (CURRENT->errors);
+		tmp = make_raw_rw_request();
+		if ( tmp < 2 ){
+			request_done(tmp);
+			continue;
+		}
+
+		if (TESTF(FD_NEED_TWADDLE))
+			twaddle();
+		floppy_tq.routine = (void *)(void *) floppy_start;
+		queue_task(&floppy_tq, &tq_timer);
+#ifdef DEBUGT
+		debugt("queue fd request");
+#endif
+		return;
+	}
+#undef REPEAT
+}
+
+static struct cont_t rw_cont={
+	rw_interrupt,
+	redo_fd_request,
+	bad_flp_intr,
+	request_done };
+
+struct tq_struct request_tq =
+{ 0, 0, (void *) (void *) redo_fd_request, 0 };
+
+static void process_fd_request(void)
+{
+	cont = &rw_cont;
+	queue_task(&request_tq, &tq_timer);
+}
+
+static void do_fd_request(void)
+{
+	if (fdc_busy){
+		/* fdc busy, this new request will be treated when the
+		   current one is done */
+		is_alive("do fd request, old request running");
+		return;
+	}
+	/* fdc_busy cannot be set by an interrupt or a bh */
+	floppy_grab_irq_and_dma();
+	fdc_busy=1;
+	reschedule_timeout(MAXTIMEOUT, "do fd request",0);
+	process_fd_request();
+	is_alive("do fd request");
+}
+
+static struct cont_t poll_cont={
+	success_and_wakeup,
+	floppy_ready,
+	generic_failure,
+	generic_done };
+
+static int poll_drive(int interruptible, int flag)
+{
+	int ret;
+	/* no auto-sense, just clear dcl */
+	raw_cmd = &default_raw_cmd;
+	raw_cmd->flags= flag;
+	raw_cmd->track=0;
+	raw_cmd->cmd_count=0;
+	cont = &poll_cont;
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("setting NEWCHANGE in poll_drive\n");
+	}
+#endif
+	SETF(FD_DISK_NEWCHANGE);
+	WAIT(floppy_ready);
+	return ret;
+}
+
+/*
+ * User triggered reset
+ * ====================
+ */
+
+static void reset_intr(void)
+{
+	printk("weird, reset interrupt called\n");
+}
+
+static struct cont_t reset_cont={
+	reset_intr,
+	success_and_wakeup,
+	generic_failure,
+	generic_done };
+
+static int user_reset_fdc(int drive, int arg, int interruptible)
+{
+	int ret;
+
+	ret=0;
+	if(arg == FD_RESET_IF_NEEDED && !FDCS->reset)
+		return 0;
+	LOCK_FDC(drive,interruptible);
+	if(arg == FD_RESET_ALWAYS)
+		FDCS->reset=1;
+	if ( FDCS->reset ){
+		cont = &reset_cont;
+		reschedule_timeout(CURRENTD, "user reset fdc", 0);
+		WAIT(reset_fdc);
+	}
+	process_fd_request();
+	return ret;
+}
+
+/*
+ * Misc Ioctl's and support
+ * ========================
+ */
+static int fd_copyout(void *param, volatile void *address, int size)
+{
+	int i;
+
+	i = verify_area(VERIFY_WRITE,param,size);
+	if (i)
+		return i;
+	memcpy_tofs(param,(void *) address, size);
+	return 0;
+}
+
+static int fd_copyin(void *param, volatile void *address, int size)
+{
+	int i;
+
+	i = verify_area(VERIFY_READ,param,size);
+	if (i)
+		return i;
+	memcpy_fromfs((void *) address, param, size);
+	return 0;
+}
+
+#define COPYOUT(x) ECALL(fd_copyout( (void *)param, &(x), sizeof(x)))
+#define COPYIN(x) ECALL(fd_copyin( (void *)param, &(x), sizeof(x)))
+
+static const char *drive_name(int type, int drive )
+{
+	struct floppy_struct *floppy;
+
+	if ( type )
+		floppy = floppy_type + type;
+	else {
+		if ( UDP->native_format )
+			floppy = floppy_type + UDP->native_format;
+		else
+			return "(null)";
+	}
+	if ( floppy->name )
+		return floppy->name;
+	else
+		return "(null)";
+}
+
+
+/* raw commands */
+static void raw_cmd_done(int flag)
+{
+	int i;
+
+	if(!flag) {
+		raw_cmd->flags = FD_RAW_FAILURE;
+		raw_cmd->flags |= FD_RAW_HARDFAILURE;
+	} else {
+		raw_cmd->reply_count = inr;
+		for( i=0; i< raw_cmd->reply_count; i++)
+			raw_cmd->reply[i] = reply_buffer[i];
+
+		if ( raw_cmd->flags & ( FD_RAW_READ | FD_RAW_WRITE ))
+			raw_cmd->length = get_dma_residue(FLOPPY_DMA);
+
+		if( (raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
+		    (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
+			raw_cmd->flags |= FD_RAW_FAILURE;
+
+		if( disk_change(current_drive) )
+			raw_cmd->flags |= FD_RAW_DISK_CHANGE;
+		else
+			raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
+		if(raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
+			motor_off_callback(current_drive);
+
+		if(raw_cmd->next &&
+		   (!(raw_cmd->flags & FD_RAW_FAILURE) ||
+		    !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
+		   ((raw_cmd->flags & FD_RAW_FAILURE) ||
+		    !(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
+			raw_cmd = raw_cmd->next;
+			return;
+		}
+	}
+	generic_done(flag);
+}
+
+
+static struct cont_t raw_cmd_cont={
+	success_and_wakeup,
+	floppy_start,
+	generic_failure,
+	raw_cmd_done
+};
+
+static inline int raw_cmd_copyout(int cmd, char *param,
+				  struct floppy_raw_cmd *ptr)
+{
+	struct old_floppy_raw_cmd old_raw_cmd;
+	int ret;
+
+	while(ptr) {
+		if(cmd == OLDFDRAWCMD) {
+			old_raw_cmd.flags = ptr->flags;
+			old_raw_cmd.data = ptr->data;
+			old_raw_cmd.length = ptr->length;
+			old_raw_cmd.rate = ptr->rate;
+			old_raw_cmd.reply_count = ptr->reply_count;
+			memcpy(old_raw_cmd.reply, ptr->reply, 7);
+			COPYOUT(old_raw_cmd);
+			param += sizeof(old_raw_cmd);
+		} else {
+			COPYOUT(*ptr);
+			param += sizeof(struct floppy_raw_cmd);
+		}
+
+		if ( (ptr->flags & FD_RAW_READ) && ptr->buffer_length){
+			if(ptr->length>=0 && ptr->length<=ptr->buffer_length)
+				ECALL(fd_copyout(ptr->data,
+						 ptr->kernel_data,
+						 ptr->buffer_length -
+						 ptr->length));
+		}
+		ptr = ptr->next;
+	}
+	return 0;
+}
+
+
+static void raw_cmd_free(struct floppy_raw_cmd **ptr)
+{
+	struct floppy_raw_cmd **next;
+
+	while(*ptr) {
+		next = & (*ptr)->next;
+		if((*ptr)->buffer_length) {
+			free_pages((unsigned long)(*ptr)->kernel_data,
+				   __get_order((*ptr)->buffer_length));
+			(*ptr)->buffer_length = 0;
+		}
+		kfree(*ptr);
+		*ptr = 0;
+		ptr = next;
+	}
+}
+
+
+static inline int raw_cmd_copyin(int cmd, char *param,
+				 struct floppy_raw_cmd **rcmd)
+{
+	struct floppy_raw_cmd *ptr;
+	struct old_floppy_raw_cmd old_raw_cmd;
+	int ret;
+	int i;
+
+	*rcmd = 0;
+	while(1) {
+		ptr = (struct floppy_raw_cmd *)
+			kmalloc(sizeof (struct floppy_raw_cmd ), GFP_USER);
+		if (!ptr)
+			return -ENOMEM;
+		ptr->next = 0;
+		ptr->buffer_length = 0;
+		*rcmd = ptr;
+		if(cmd == OLDFDRAWCMD){
+			COPYIN(old_raw_cmd);
+			ptr->flags = old_raw_cmd.flags;
+			ptr->data = old_raw_cmd.data;
+			ptr->length = old_raw_cmd.length;
+			ptr->rate = old_raw_cmd.rate;
+			ptr->cmd_count = old_raw_cmd.cmd_count;
+			ptr->track = old_raw_cmd.track;
+			memcpy(ptr->cmd, old_raw_cmd.cmd, 9);
+			if(ptr->cmd_count > 9)
+				return -EINVAL;
+			ptr->next = 0;
+			ptr->phys_length = 0;
+			param += sizeof(struct old_floppy_raw_cmd);
+		} else {
+			COPYIN(*ptr);
+			param += sizeof(struct floppy_raw_cmd);
+			if(ptr->cmd_count > 16)
+				return -EINVAL;
+		}
+
+		for(i=0; i< 16; i++)
+			ptr->reply[i] = 0;
+		ptr->resultcode = 0;
+
+		ptr->next = 0;
+		ptr->buffer_length = 0;
+		ptr->kernel_data = 0;
+
+		if(ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
+			if(!ptr->length)
+				return -EINVAL;
+
+			ptr->kernel_data =(char *)dma_mem_alloc(ptr->length);
+			if(!ptr->kernel_data)
+				return -ENOMEM;
+			ptr->buffer_length = ptr->length;
+		}
+		if(ptr->flags & FD_RAW_WRITE)
+			fd_copyin(ptr->data, ptr->kernel_data, ptr->length);
+		rcmd = & (ptr->next);
+		if( ! (ptr->flags & FD_RAW_MORE))
+			return 0;
+		ptr->rate &= 0x03;
+
+	}
+}
+
+
+static int raw_cmd_ioctl(int cmd, void *param)
+{
+	int drive, ret, ret2;
+	struct floppy_raw_cmd *my_raw_cmd;
+
+	if ( FDCS->rawcmd <= 1 )
+		FDCS->rawcmd = 1;
+	for ( drive= 0; drive < N_DRIVE; drive++){
+		if ( FDC(drive) != fdc)
+			continue;
+		if ( drive == current_drive ){
+			if ( UDRS->fd_ref > 1 ){
+				FDCS->rawcmd = 2;
+				break;
+			}
+		} else if ( UDRS->fd_ref ){
+			FDCS->rawcmd = 2;
+			break;
+		}
+	}
+
+	if(FDCS->reset)
+		return -EIO;
+
+	ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
+	if (ret) {
+		raw_cmd_free(&my_raw_cmd);
+		return ret;
+	}
+	
+	raw_cmd = my_raw_cmd;
+	cont = &raw_cmd_cont;
+	ret=wait_til_done(floppy_start,1);
+#ifdef DCL_DEBUG
+	if (DP->flags & FD_DEBUG){
+		DPRINT("calling disk change from raw_cmd ioctl\n");
+	}
+#endif
+
+	if (ret != -EINTR && FDCS->reset)
+		ret = -EINTR;
+
+	DRS->track = NO_TRACK;
+
+	ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
+	if(!ret)
+		ret = ret2;
+	raw_cmd_free(&my_raw_cmd);
+	return ret;
+}
+
+static int invalidate_drive(kdev_t rdev)
+{
+	/* invalidate the buffer track to force a reread */
+	set_bit( DRIVE(rdev), &fake_change);
+	process_fd_request();
+	check_disk_change(rdev);
+	return 0;
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long param)
+{
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
+
+	struct floppy_struct newparams;
+	struct format_descr tmp_format_req;
+	int i,drive,type,cnt;
+	kdev_t device;
+	struct floppy_struct *this_floppy;
+	const char *name;
+	int ret;
+
+	device = inode->i_rdev;
+	switch (cmd) {
+		RO_IOCTLS(device,param);
+	}
+	type = TYPE(device);
+	drive = DRIVE(device);
+	switch (cmd) {
+		case OLDFDGETDRVTYP:
+		case FDGETDRVTYP:
+			i=verify_area(VERIFY_WRITE,(void *) param,16);
+			if (i)
+				return i;
+			name = drive_name(type,drive);
+			for ( cnt=0; cnt<16; cnt++){
+				put_user(name[cnt], ((char*)param)+cnt);
+				if ( ! *name )
+					break;
+			}
+			return 0;
+		case OLDFDGETMAXERRS:
+		case FDGETMAXERRS:
+			COPYOUT(UDP->max_errors);
+			return 0;
+		case OLDFDGETPRM:
+		case FDGETPRM:
+			if (type)
+				this_floppy = &floppy_type[type];
+			else if ((this_floppy = current_type[drive]) == NULL)
+				return -ENODEV;
+			COPYOUT(this_floppy[0]);
+			return 0;
+		case OLDFDPOLLDRVSTAT:
+		case FDPOLLDRVSTAT:
+			LOCK_FDC(drive,1);
+			CALL(poll_drive(1, FD_RAW_NEED_DISK));
+			process_fd_request();
+			/* fall through */
+		case OLDFDGETDRVSTAT:
+		case FDGETDRVSTAT:
+			COPYOUT(*UDRS);
+			return 0;
+		case OLDFDGETFDCSTAT:
+			COPYOUT(* (struct old_floppy_fdc_state *) UFDCS);
+			return 0;
+		case FDGETFDCSTAT:
+			COPYOUT(*UFDCS);
+			return 0;
+		case OLDFDGETDRVPRM:
+		case FDGETDRVPRM:
+			COPYOUT(*UDP);
+			return 0;
+		case OLDFDWERRORGET:
+		case FDWERRORGET:
+			COPYOUT(*UDRWE);
+			return 0;
+	}
+	if (!IOCTL_ALLOWED)
+		return -EPERM;
+	switch (cmd) {
+		case OLDFDWERRORCLR:
+		case FDWERRORCLR:
+			UDRWE->write_errors = 0;
+			UDRWE->first_error_sector = 0;
+			UDRWE->first_error_generation = 0;
+			UDRWE->last_error_sector = 0;
+			UDRWE->last_error_generation = 0;
+			UDRWE->badness = 0;
+			return 0;
+        	case OLDFDRAWCMD:
+		case FDRAWCMD:
+			if (type)
+				return -EINVAL;
+			LOCK_FDC(drive,1);
+			set_floppy(device);
+			CALL(i = raw_cmd_ioctl(cmd,(void *) param));
+			process_fd_request();
+			return i;
+		case OLDFDFMTTRK:
+		case FDFMTTRK:
+			if (UDRS->fd_ref != 1)
+				return -EBUSY;
+			COPYIN(tmp_format_req);
+			return do_format(device, &tmp_format_req);
+		case OLDFDSETMAXERRS:
+		case FDSETMAXERRS:
+			COPYIN(UDP->max_errors);
+			return 0;
+		case OLDFDFMTBEG:
+		case FDFMTBEG:
+			return 0;
+		case OLDFDCLRPRM:
+		case FDCLRPRM:
+			LOCK_FDC(drive,1);
+			current_type[drive] = NULL;
+			floppy_sizes[drive] = MAX_DISK_SIZE;
+			UDRS->keep_data = 0;
+			return invalidate_drive(device);
+		case OLDFDFMTEND:
+		case FDFMTEND:
+		case OLDFDFLUSH:
+		case FDFLUSH:
+			LOCK_FDC(drive,1);
+			return invalidate_drive(device);
+		case OLDFDSETPRM:
+		case FDSETPRM:
+		case OLDFDDEFPRM:
+		case FDDEFPRM:
+			COPYIN(newparams);
+			/* sanity checking for parameters.*/
+			if(newparams.sect <= 0 ||
+			   newparams.head <= 0 ||
+			   newparams.track <= 0 ||
+			   newparams.track > UDP->tracks>> STRETCH(&newparams) ||
+			   /* check if reserved bits are set */
+			   (newparams.stretch & ~(FD_STRETCH | FD_SWAPSIDES)) != 0)
+				return -EINVAL;
+			if ( type){
+				if ( !suser() )
+					return -EPERM;
+				LOCK_FDC(drive,1);
+				for ( cnt = 0; cnt < N_DRIVE; cnt++){
+					if (TYPE(drive_state[cnt].fd_device) == type &&
+					    drive_state[cnt].fd_ref)
+						set_bit(drive, &fake_change);
+				}
+				floppy_type[type] = newparams;
+				floppy_type[type].name="user format";
+				for (cnt = type << 2 ;
+				     cnt < (type << 2 ) + 4 ;
+				     cnt++)
+					floppy_sizes[cnt]=
+						floppy_sizes[cnt+0x80]=
+						floppy_type[type].size>>1;
+				process_fd_request();
+				for ( cnt = 0; cnt < N_DRIVE; cnt++){
+					if (TYPE(drive_state[cnt].fd_device) == type &&
+					    drive_state[cnt].fd_ref)
+						check_disk_change(drive_state[cnt].
+								  fd_device);
+				}
+				return 0;
+			}
+
+			LOCK_FDC(drive,1);
+			if ( cmd != FDDEFPRM )
+				/* notice a disk change immediately, else
+				 * we loose our settings immediately*/
+				CALL(poll_drive(1,0));
+			user_params[drive] = newparams;
+			if (buffer_drive == drive &&
+			    buffer_max > user_params[drive].sect)
+				buffer_max=user_params[drive].sect;
+			current_type[drive] = &user_params[drive];
+			floppy_sizes[drive] = user_params[drive].size >> 1;
+			if (cmd == FDDEFPRM)
+				DRS->keep_data = -1;
+			else
+				DRS->keep_data = 1;
+			/* invalidation. Invalidate only when needed, i.e.
+			 * when there are already sectors in the buffer cache
+			 * whose number will change. This is useful, because
+			 * mtools often changes the geometry of the disk after
+			 * looking at the boot block */
+			if (DRS->maxblock >
+			    user_params[drive].sect ||
+			    DRS->maxtrack )
+				invalidate_drive(device);
+			else
+				process_fd_request();
+			return 0;
+		case OLDFDRESET:
+		case FDRESET:
+			return user_reset_fdc( drive, (int)param, 1);
+		case OLDFDMSGON:
+		case FDMSGON:
+			UDP->flags |= FTD_MSG;
+			return 0;
+		case OLDFDMSGOFF:
+		case FDMSGOFF:
+			UDP->flags &= ~FTD_MSG;
+			return 0;
+		case OLDFDSETEMSGTRESH:
+		case FDSETEMSGTRESH:
+			UDP->max_errors.reporting =
+			(unsigned short) (param & 0x0f);
+			return 0;
+		case OLDFDTWADDLE:
+		case FDTWADDLE:
+			LOCK_FDC(drive,1);
+			twaddle();
+			process_fd_request();
+	}
+	if ( ! suser() )
+		return -EPERM;
+	switch(cmd){
+		case OLDFDSETDRVPRM:
+		case FDSETDRVPRM:
+			COPYIN(*UDP);
+			return 0;
+		default:
+			return -EINVAL;
+	}
+	return 0;
+#undef IOCTL_ALLOWED
+}
+
+static void config_types(void)
+{
+	int first=1;
+	int drive;
+
+	/* read drive info out of physical cmos */
+	drive=0;
+	if (!UDP->cmos )
+		UDP->cmos= FLOPPY0_TYPE;
+	drive=1;
+	if (!UDP->cmos && FLOPPY1_TYPE)
+		UDP->cmos = FLOPPY1_TYPE;
+
+	/* XXX */
+	/* additional physical CMOS drive detection should go here */
+
+	for (drive=0; drive < N_DRIVE; drive++){
+		if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
+			memcpy((char *) UDP,
+			       (char *) (&default_drive_params[(int)UDP->cmos].params),
+			       sizeof(struct floppy_drive_params));
+		if (UDP->cmos){
+			if (first)
+				printk("Floppy drive(s): ");
+			else
+				printk(", ");
+			first=0;
+			if (UDP->cmos > 0 ){
+				ALLOWED_DRIVE_MASK |= 1 << drive;
+				printk("fd%d is %s", drive,
+					default_drive_params[(int)UDP->cmos].name);
+			} else
+				printk("fd%d is unknown type %d",drive,
+				       UDP->cmos);
+		}
+	}
+	if(!first)
+		printk("\n");
+}
+
+static int floppy_read(struct inode * inode, struct file * filp,
+		       char * buf, int count)
+{
+	int drive = DRIVE(inode->i_rdev);
+
+	check_disk_change(inode->i_rdev);
+	if (UTESTF(FD_DISK_CHANGED))
+		return -ENXIO;
+	return block_read(inode, filp, buf, count);
+}
+
+static int floppy_write(struct inode * inode, struct file * filp,
+		        const char * buf, int count)
+{
+	int block;
+	int ret;
+	int drive = DRIVE(inode->i_rdev);
+
+	if (!UDRS->maxblock)
+		UDRS->maxblock=1;/* make change detectable */
+	check_disk_change(inode->i_rdev);
+	if (UTESTF(FD_DISK_CHANGED))
+		return -ENXIO;
+	if (!UTESTF(FD_DISK_WRITABLE))
+		return -EROFS;
+	block = (filp->f_pos + count) >> 9;
+	if (block > UDRS->maxblock)
+		UDRS->maxblock = block;
+	ret= block_write(inode, filp, buf, count);
+	return ret;
+}
+
+static void floppy_release(struct inode * inode, struct file * filp)
+{
+	int drive;
+
+	drive = DRIVE(inode->i_rdev);
+
+	if( !filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+		/* if the file is mounted OR (writable now AND writable at
+		 * open time) Linus: Does this cover all cases? */
+		block_fsync(inode,filp);
+
+	if (UDRS->fd_ref < 0)
+		UDRS->fd_ref=0;
+	else if (!UDRS->fd_ref--) {
+		DPRINT("floppy_release with fd_ref == 0");
+		UDRS->fd_ref = 0;
+	}
+	floppy_release_irq_and_dma();
+}
+
+/*
+ * floppy_open check for aliasing (/dev/fd0 can be the same as
+ * /dev/PS0 etc), and disallows simultaneous access to the same
+ * drive with different device numbers.
+ */
+#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
+
+static int floppy_open(struct inode * inode, struct file * filp)
+{
+	int drive;
+	int old_dev;
+	int try;
+	char *tmp;
+
+	if (!filp) {
+		DPRINT("Weird, open called with filp=0\n");
+		return -EIO;
+	}
+
+	drive = DRIVE(inode->i_rdev);
+
+	if (drive >= N_DRIVE ||
+	    !( ALLOWED_DRIVE_MASK & ( 1 << drive)) ||
+	    fdc_state[FDC(drive)].version == FDC_NONE)
+		return -ENXIO;
+
+	if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
+		return -ENXIO;
+	old_dev = UDRS->fd_device;
+	if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
+		return -EBUSY;
+
+	if(!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
+		USETF(FD_DISK_CHANGED);
+		USETF(FD_VERIFY);
+	}
+
+	if(UDRS->fd_ref == -1 ||
+	   (UDRS->fd_ref && (filp->f_flags & O_EXCL)))
+		return -EBUSY;
+
+	if (floppy_grab_irq_and_dma())
+		return -EBUSY;
+
+	if (filp->f_flags & O_EXCL)
+		UDRS->fd_ref = -1;
+	else
+		UDRS->fd_ref++;
+
+	if (!floppy_track_buffer){
+		/* if opening an ED drive, reserve a big buffer,
+		 * else reserve a small one */
+		if ((UDP->cmos == 6) || (UDP->cmos == 5))
+			try = 64; /* Only 48 actually useful */
+		else
+			try = 32; /* Only 24 actually useful */
+
+		tmp=(char *)dma_mem_alloc(1024 * try);
+		if (!tmp) {
+			try >>= 1; /* buffer only one side */
+			if (try < 16)
+				try=16;
+			tmp= (char *)dma_mem_alloc(1024*try);
+		}
+		if (!tmp) {
+			DPRINT("Unable to allocate DMA memory\n");
+			RETERR(ENXIO);
+		}
+		if (floppy_track_buffer){
+			free_pages((unsigned long)tmp,__get_order(try*1024));
+		}else {
+			floppy_track_buffer = tmp;
+			max_buffer_sectors = try;
+		}
+	}
+
+	UDRS->fd_device = MINOR(inode->i_rdev);
+
+	if (old_dev && old_dev != inode->i_rdev) {
+		if (buffer_drive == drive)
+			buffer_track = -1;
+		invalidate_buffers(old_dev);
+	}
+
+	/* Allow ioctls if we have write-permissions even if read-only open */
+	if ((filp->f_mode & 2) || (permission(inode,2) == 0))
+		filp->f_mode |= IOCTL_MODE_BIT;
+	if (filp->f_mode & 2)
+		filp->f_mode |= OPEN_WRITE_BIT;
+
+	if (UFDCS->rawcmd == 1)
+	       UFDCS->rawcmd = 2;
+
+	if (filp->f_flags & O_NDELAY)
+		return 0;
+	if (filp->f_mode & 3) {
+		UDRS->last_checked = 0;
+		check_disk_change(inode->i_rdev);
+		if (UTESTF(FD_DISK_CHANGED))
+			RETERR(ENXIO);
+	}
+	if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
+		RETERR(EROFS);
+	return 0;
+#undef RETERR
+}
+
+/*
+ * Check if the disk has been changed or if a change has been faked.
+ */
+static int check_floppy_change(kdev_t dev)
+{
+	int drive = DRIVE( dev );
+
+	if (MAJOR(dev) != MAJOR_NR) {
+		DPRINT("floppy_changed: not a floppy\n");
+		return 0;
+	}
+
+	if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
+		return 1;
+
+	if(UDRS->last_checked + UDP->checkfreq < jiffies){
+		lock_fdc(drive,0);
+		poll_drive(0,0);
+		process_fd_request();
+	}
+
+	if(UTESTF(FD_DISK_CHANGED) ||
+	   UTESTF(FD_VERIFY) ||
+	   test_bit(drive, &fake_change) ||
+	   (!TYPE(dev) && !current_type[drive]))
+		return 1;
+	return 0;
+}
+
+/* revalidate the floppy disk, i.e. trigger format autodetection by reading
+ * the bootblock (block 0). "Autodetection" is also needed to check whether
+ * there is a disk in the drive at all... Thus we also do it for fixed
+ * geometry formats */
+static int floppy_revalidate(kdev_t dev)
+{
+#define NO_GEOM (!current_type[drive] && !TYPE(dev))
+	struct buffer_head * bh;
+	int drive=DRIVE(dev);
+	int cf;
+
+	if(UTESTF(FD_DISK_CHANGED) ||
+	   UTESTF(FD_VERIFY) ||
+	   test_bit(drive, &fake_change) ||
+	   NO_GEOM){
+		lock_fdc(drive,0);
+		cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
+		if(! (cf || test_bit(drive, &fake_change) || NO_GEOM)){
+			process_fd_request(); /* already done by another thread */
+			return 0;
+		}
+		UDRS->maxblock = 0;
+		UDRS->maxtrack = 0;
+		if ( buffer_drive == drive)
+			buffer_track = -1;
+		clear_bit(drive, &fake_change);
+		UCLEARF(FD_DISK_CHANGED);
+		if(cf)
+			UDRS->generation++;
+		if(NO_GEOM){
+			/* auto-sensing */
+			int size = floppy_blocksizes[MINOR(dev)];
+			if (!size)
+				size = 1024;
+			if (!(bh = getblk(dev,0,size))){
+				process_fd_request();
+				return 1;
+			}
+			if ( bh && ! bh->b_uptodate)
+				ll_rw_block(READ, 1, &bh);
+			process_fd_request();
+			wait_on_buffer(bh);
+			brelse(bh);
+			return 0;
+		}
+		if(cf)
+			poll_drive(0, FD_RAW_NEED_DISK);
+		process_fd_request();
+	}
+	return 0;
+}
+
+static struct file_operations floppy_fops = {
+	NULL,			/* lseek - default */
+	floppy_read,		/* read - general block-dev read */
+	floppy_write,		/* write - general block-dev write */
+	NULL,		       	/* readdir - bad */
+	NULL,			/* select */
+	fd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	floppy_open,		/* open */
+	floppy_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	check_floppy_change,	/* media_change */
+	floppy_revalidate,	/* revalidate */
+};
+
+/*
+ * Floppy Driver initialisation
+ * =============================
+ */
+
+/* Determine the floppy disk controller type */
+/* This routine was written by David C. Niemi */
+static char get_fdc_version(void)
+{
+	int r;
+
+	output_byte(FD_DUMPREGS);	/* 82072 and better know DUMPREGS */
+	if ( FDCS->reset )
+		return FDC_NONE;
+	if ( (r = result()) <= 0x00)
+		return FDC_NONE;	/* No FDC present ??? */
+	if ((r==1) && (reply_buffer[0] == 0x80)){
+		printk("FDC %d is a 8272A\n",fdc);
+		return FDC_8272A;		/* 8272a/765 don't know DUMPREGS */
+	}
+	if (r != 10) {
+		printk("FDC init: DUMPREGS: unexpected return of %d bytes.\n", r);
+		return FDC_UNKNOWN;
+	}
+	output_byte(FD_VERSION);
+	r = result();
+	if ((r == 1) && (reply_buffer[0] == 0x80)){
+		printk("FDC %d is a 82072\n",fdc);
+		return FDC_82072;		/* 82072 doesn't know VERSION */
+	}
+	if ((r != 1) || (reply_buffer[0] != 0x90)) {
+		printk("FDC init: VERSION: unexpected return of %d bytes.\n", r);
+		return FDC_UNKNOWN;
+	}
+	output_byte(FD_UNLOCK);
+	r = result();
+	if ((r == 1) && (reply_buffer[0] == 0x80)){
+		printk("FDC %d is a pre-1991 82077\n", fdc);
+		return FDC_82077_ORIG;	/* Pre-1991 82077 doesn't know LOCK/UNLOCK */
+	}
+	if ((r != 1) || (reply_buffer[0] != 0x00)) {
+		printk("FDC init: UNLOCK: unexpected return of %d bytes.\n", r);
+		return FDC_UNKNOWN;
+	}
+	printk("FDC %d is a post-1991 82077\n",fdc);
+	return FDC_82077;	/* Revised 82077AA passes all the tests */
+} /* get_fdc_version */
+
+/* lilo configuration */
+
+/* we make the invert_dcl function global. One day, somebody might
+want to centralize all thinkpad related options into one lilo option,
+there are just so many thinkpad related quirks! */
+void floppy_invert_dcl(int *ints,int param)
+{
+	int i;
+
+	for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+		if (param)
+			default_drive_params[i].params.flags |= 0x80;
+		else
+			default_drive_params[i].params.flags &= ~0x80;
+	}
+	DPRINT("Configuring drives for inverted dcl\n");
+}
+
+static void daring(int *ints,int param)
+{
+	int i;
+
+	for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+		if (param){
+			default_drive_params[i].params.select_delay = 0;
+			default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
+		} else {
+			default_drive_params[i].params.select_delay = 2*HZ/100;
+			default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
+		}
+	}
+	DPRINT1("Assuming %s floppy hardware\n", param ? "standard" : "broken");
+}
+
+static void allow_drives(int *ints, int param)
+{
+	ALLOWED_DRIVE_MASK=param;
+	DPRINT1("setting allowed_drive_mask to 0x%x\n", param);
+}
+
+static void fdc2_adr(int *ints, int param)
+{
+	FDC2 = param;
+	if(param){
+		DPRINT1("enabling second fdc at address 0x%3x\n", FDC2);
+	} else
+		DPRINT("disabling second fdc\n");
+}
+
+static void unex(int *ints,int param)
+{
+	print_unex = param;
+	DPRINT1("%sprinting messages for unexpected interrupts\n",
+		param ? "" : "not ");
+}
+
+static void set_cmos(int *ints, int dummy)
+{
+	int current_drive=0;
+
+	if ( ints[0] != 2 ){
+		DPRINT("wrong number of parameters for cmos\n");
+		return;
+	}
+	current_drive = ints[1];
+	if (current_drive < 0 || current_drive >= 8 ){
+		DPRINT("bad drive for set_cmos\n");
+		return;
+	}
+	if(current_drive >= 4 && !FDC2)
+		fdc2_adr(0, 0x370);
+	if(ints[2] <= 0 || ints[2] >= NUMBER(default_drive_params)){
+		DPRINT1("bad cmos code %d\n", ints[2]);
+		return;
+	}
+	DP->cmos = ints[2];
+	DPRINT1("setting cmos code to %d\n", ints[2]);
+}
+
+static struct param_table {
+	const char *name;
+	void (*fn)(int *ints, int param);
+	int def_param;
+} config_params[]={
+	{ "allowed_drive_mask", allow_drives, 0xff },
+	{ "all_drives", allow_drives, 0xff },
+	{ "asus_pci", allow_drives, 0x33 },
+
+	{ "daring", daring, 1 },
+
+	{ "two_fdc", fdc2_adr, 0x370 },
+	{ "one_fdc", fdc2_adr, 0 },
+
+	{ "thinkpad", floppy_invert_dcl, 1 },
+
+	{ "cmos", set_cmos, 0 },
+
+	{ "unexpected_interrupts", unex, 1 },
+	{ "no_unexpected_interrupts", unex, 0 },
+	{ "L40SX", unex, 0 } };
+
+#define FLOPPY_SETUP
+void floppy_setup(char *str, int *ints)
+{
+	int i;
+	int param;
+	if(str)
+		for(i=0; i< ARRAY_SIZE(config_params); i++){
+			if (strcmp(str,config_params[i].name) == 0 ){
+				if (ints[0] )
+					param = ints[1];
+				else
+					param = config_params[i].def_param;
+				config_params[i].fn(ints,param);
+				return;
+			}
+		}
+	if(str) {
+		DPRINT1("unknown floppy option [%s]\n", str);
+
+		DPRINT("allowed options are:");
+		for(i=0; i< ARRAY_SIZE(config_params); i++)
+			printk(" %s",config_params[i].name);
+		printk("\n");
+	} else
+		DPRINT("botched floppy option\n");
+	DPRINT("Read linux/arch/arm/drivers/block/README.fd\n");
+}
+
+int floppy_init(void)
+{
+	int i,unit,drive;
+	int have_no_fdc=0;
+
+	raw_cmd = NULL;
+
+	sti();
+
+	if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+		printk("Unable to get major %d for floppy\n",MAJOR_NR);
+		return -EBUSY;
+	}
+
+	for(i=0; i<256; i++)
+		if (TYPE(i))
+			floppy_sizes[i] = floppy_type[TYPE(i)].size >> 1;
+		else
+			floppy_sizes[i] = MAX_DISK_SIZE;
+
+	blk_size[MAJOR_NR] = floppy_sizes;
+	blksize_size[MAJOR_NR] = floppy_blocksizes;
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
+	config_types();
+
+	fdc_state[0].address = FDC1;
+	fdc_state[0].dor = 0;
+#if N_FDC > 1
+	fdc_state[1].address = FDC2;
+	fdc_state[1].dor = 0;
+#endif
+
+	for (i = 0 ; i < N_FDC ; i++) {
+		fdc = i;
+		FDCS->dtr = -1;
+		FDCS->dor = 0x4;
+		FDCS->reset = 0;
+		FDCS->version = FDC_NONE;
+	}
+
+	if(floppy_grab_irq_and_dma()){
+		unregister_blkdev(MAJOR_NR,"fd");
+		return -EBUSY;
+	}
+
+	/* initialise drive state */
+	for (drive = 0; drive < N_DRIVE ; drive++) {
+		UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
+		UDRS->generation = 0;
+		UDRS->keep_data = 0;
+		UDRS->fd_ref = 0;
+		UDRS->fd_device = 0;
+		floppy_track_buffer = NULL;
+		max_buffer_sectors = 0;
+		UDRWE->write_errors = 0;
+		UDRWE->first_error_sector = 0;
+		UDRWE->first_error_generation = 0;
+		UDRWE->last_error_sector = 0;
+		UDRWE->last_error_generation = 0;
+		UDRWE->badness = 0;
+	}
+
+	for (i = 0 ; i < N_FDC ; i++) {
+		fdc = i;
+		FDCS->driver_version = FD_DRIVER_VERSION;
+		for(unit=0; unit<4; unit++)
+			FDCS->track[unit] = 0;
+		if (FDCS->address == -1 )
+			continue;
+		FDCS->rawcmd = 2;
+		if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0)){
+			FDCS->address = -1;
+			continue;
+		}
+		/* Try to determine the floppy controller type */
+		FDCS->version = get_fdc_version();
+		if (FDCS->version == FDC_NONE){
+			FDCS->address = -1;
+			continue;
+		}
+
+		request_region(FDCS->address, 6, "floppy");
+		request_region(FDCS->address+7, 1, "floppy DIR");
+		/* address + 6 is reserved, and may be taken by IDE.
+		 * Unfortunately, Adaptec doesn't know this :-(, */
+
+		have_no_fdc = 0;
+		/* Not all FDCs seem to be able to handle the version command
+		 * properly, so force a reset for the standard FDC clones,
+		 * to avoid interrupt garbage.
+		 */
+		FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG;
+		user_reset_fdc(-1,FD_RESET_ALWAYS,0);
+	}
+	fdc=0;
+	del_timer(&fd_timeout);
+	current_drive = 0;
+	floppy_release_irq_and_dma();
+	initialising=0;
+	if(have_no_fdc)
+		unregister_blkdev(MAJOR_NR,"fd");
+	return have_no_fdc;
+}
+
+static int floppy_grab_irq_and_dma(void)
+{
+	int i;
+	cli();
+	if (usage_count++){
+		sti();
+		return 0;
+	}
+	sti();
+#ifdef FD_MODULE
+	MOD_INC_USE_COUNT;
+#endif
+	for(i=0; i< N_FDC; i++){
+		if(FDCS->address != -1){
+			fdc = i;
+			reset_fdc_info(1);
+			arm_set_dor(FDCS->dor);
+		}
+	}
+	set_dor(0, ~0, 8);  /* avoid immediate interrupt */
+
+	if (request_irq(FLOPPY_IRQ, floppy_interrupt, 
+			SA_INTERRUPT|SA_SAMPLE_RANDOM, "floppy")) {
+		DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
+			FLOPPY_IRQ);
+		return -1;
+	}
+	if (request_dma(FLOPPY_DMA,"floppy")) {
+		DPRINT1("Unable to grab DMA%d for the floppy driver\n",
+			FLOPPY_DMA);
+		free_irq(FLOPPY_IRQ);
+		return -1;
+	}
+	for(fdc = 0; fdc < N_FDC ; fdc++)
+		if(FDCS->address != -1)
+			arm_set_dor(FDCS->dor);
+	fdc = 0;
+	enable_irq(FLOPPY_IRQ);
+	return 0;
+}
+
+static void floppy_release_irq_and_dma(void)
+{
+#ifdef CONFIG_FLOPPY_SANITY
+	int drive;
+#endif
+	long tmpsize;
+	void *tmpaddr;
+
+	cli();
+	if (--usage_count){
+		sti();
+		return;
+	}
+	sti();
+#ifdef FD_MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+	disable_dma(FLOPPY_DMA);
+	free_dma(FLOPPY_DMA);
+	disable_irq(FLOPPY_IRQ);
+	free_irq(FLOPPY_IRQ);
+
+	set_dor(0, ~0, 8);
+#if N_FDC > 1
+	set_dor(1, ~8, 0);
+#endif
+	floppy_enable_hlt();
+
+	if (floppy_track_buffer && max_buffer_sectors) {
+		tmpsize = max_buffer_sectors*1024;
+		tmpaddr = (void *)floppy_track_buffer;
+		floppy_track_buffer = NULL;
+		max_buffer_sectors = 0;
+		free_pages((unsigned long)tmpaddr, __get_order(tmpsize));
+	}
+
+#ifdef CONFIG_FLOPPY_SANITY
+	for(drive=0; drive < N_FDC * 4; drive++)
+		if( motor_off_timer[drive].next )
+			printk("motor off timer %d still active\n", drive);
+
+	if(fd_timeout.next)
+		printk("floppy timer still active:%s\n", timeout_message);
+	if (fd_timer.next)
+		printk("auxillary floppy timer still active\n");
+	if(floppy_tq.sync)
+		printk("task queue still active\n");
+#endif
+}
+
+
+#ifdef MODULE
+
+extern char *get_options(char *str, int *ints);
+
+static void mod_setup(char *pattern, void (*setup)(char *, int *))
+{
+	int i;
+	char c;
+	int j;
+	int match;
+	char buffer[100];
+	int ints[11];
+	int length = strlen(pattern)+1;
+
+	match=0;
+	j=1;
+
+	for(i=current->mm->env_start; i< current->mm->env_end; i ++){
+		c= get_fs_byte(i);
+		if(match){
+			if(j==99)
+				c='\0';
+			buffer[j] = c;
+			if(!c || c == ' ' || c == '\t'){
+				if(j){
+					buffer[j] = '\0';
+					setup(get_options(buffer,ints),ints);
+				}
+				j=0;
+			} else
+				j++;
+			if(!c)
+				break;
+			continue;
+		}
+		if( (!j && !c) || ( j && c == pattern[j-1]))
+			j++;
+		else
+			j=0;
+		if(j==length){
+			match=1;
+			j=0;
+		}
+	}
+}
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int init_module(void)
+{
+	int ret;
+	printk("inserting floppy driver for %s\n", kernel_version);
+
+	mod_setup("floppy=", floppy_setup);
+
+	ret = floppy_init();
+		return 0;
+}
+
+void cleanup_module(void)
+{
+	int fdc;
+
+	for(fdc=0; fdc<2; fdc++)
+		if (FDCS->address != -1){
+			release_region(FDCS->address, 6);
+			release_region(FDCS->address+7, 1);
+		}
+
+	unregister_blkdev(MAJOR_NR, "fd");
+
+	blk_dev[MAJOR_NR].request_fn = 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -urNwbB linux/arch/arm/drivers/block/genhd.c linux.arm/arch/arm/drivers/block/genhd.c
--- linux/arch/arm/drivers/block/genhd.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/genhd.c	Sun Mar  3 12:22:14 1996
@@ -0,0 +1,506 @@
+/*
+ *  Code extracted from
+ *  linux/kernel/hd.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ *  in the early extended-partition checks and added DM partitions
+ *
+ *  Support for DiskManager v6.0x added by Mark Lord (mlord@bnr.ca)
+ *  with information provided by OnTrack.  This now works for linux fdisk
+ *  and LILO, as well as loadlin and bootln.  Note that disks other than
+ *  /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
+ *
+ *  More flexible handling of extended partitions - aeb, 950831
+ *
+ *  Modifications Copyright (C) 1995, 1996 Russell King
+ *
+ *  Origional from linux/drivers/genhd.c
+ *
+ *  Altered to find image files on ADFS-formatted hard drives.
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+
+struct gendisk *gendisk_head = NULL;
+
+static int current_minor = 0;
+extern int *blk_size[];
+extern void rd_load(void);
+extern int ramdisk_size;
+
+extern int chr_dev_init(void);
+extern int blk_dev_init(void);
+extern int scsi_dev_init(void);
+extern int net_dev_init(void);
+
+static void print_minor_name (struct gendisk *hd, int minor)
+{
+	unsigned int unit = minor >> hd->minor_shift;
+	unsigned int part = minor & ((1 << hd->minor_shift) - 1);
+
+#ifdef CONFIG_BLK_DEV_IDE
+	/*
+	 * IDE devices use multiple major numbers, but the drives
+	 * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
+	 * This requires some creative handling here to find the
+	 * correct name to use, with some help from ide.c
+	 */
+	if (!strcmp(hd->major_name, "ide")) {
+		char name[16];		/* more than large enough */
+		strcpy(name, hd->real_devices); /* courtesy ide.c */
+		name[strlen(name)-1] += unit;
+		printk(" %s", name);
+	} else
+#endif
+		printk(" %s%c", hd->major_name, 'a' + unit);
+	if (part)
+		printk("%d", part);
+	else
+		printk(":");
+}
+
+static void add_partition (struct gendisk *hd, int minor, int start, int size)
+{
+	hd->part[minor].start_sect = start;
+	hd->part[minor].nr_sects   = size;
+	print_minor_name(hd, minor);
+}
+
+static inline unsigned long get_word(const unsigned int *addr)
+{
+	const unsigned char *ac = (const unsigned char *)addr;
+	if ((int)addr & 3)
+		return ac[0] | ac[1]<<8 | ac[2]<<16 | ac[3]<<24;
+	else
+		return addr[0];
+}
+
+#ifdef CONFIG_MSDOS_PARTITION
+static inline int is_extended_partition(struct partition *p)
+{
+	return (p->sys_ind == DOS_EXTENDED_PARTITION ||
+		p->sys_ind == LINUX_EXTENDED_PARTITION);
+}
+
+/*
+ * Create devices for each logical partition in an extended partition.
+ * The logical partitions form a linked list, with each entry being
+ * a partition table with two entries.  The first entry
+ * is the real data partition (with a start relative to the partition
+ * table start).  The second is a pointer to the next logical partition
+ * (with a start relative to the entire extended partition).
+ * We do not create a Linux partition for the partition tables, but
+ * only for the actual data partitions.
+ */
+
+static void extended_partition(struct gendisk *hd, kdev_t dev)
+{
+	struct buffer_head *bh;
+	struct partition *p;
+	unsigned long first_sector, first_size, this_sector, this_size;
+	unsigned long start_sect, nr_sects;
+	int mask = (1 << hd->minor_shift) - 1;
+	int i;
+
+	first_sector = hd->part[MINOR(dev)].start_sect;
+	first_size = hd->part[MINOR(dev)].nr_sects;
+	this_sector = first_sector;
+
+	while (1) {
+		if ((current_minor & mask) == 0)
+			return;
+		if (!(bh = bread(dev,0,1024)))
+			return;
+	  /*
+	   * This block is from a device that we're about to stomp on.
+	   * So make sure nobody thinks this block is usable.
+	   */
+		bh->b_dirt = 0;
+		bh->b_uptodate = 0;
+		bh->b_req = 0;
+		
+		if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
+			goto done;
+
+		p = (struct partition *) (0x1BE + bh->b_data);
+
+		this_size = hd->part[MINOR(dev)].nr_sects;
+
+		/*
+		 * Usually, the first entry is the real data partition,
+		 * the 2nd entry is the next extended partition, or empty,
+		 * and the 3rd and 4th entries are unused.
+		 * However, DRDOS sometimes has an extended partition as
+		 * the first entry (when the data partition is empty),
+		 * and OS/2 seems to use all four entries.
+		 */
+		 
+		/*
+		 * First process the data partition(s)
+		 */
+		for (i = 0; i < 4; i++, p++) {
+			nr_sects = get_word (&p->nr_sects);
+			if (!nr_sects || is_extended_partition(p))
+				continue;
+
+			/*
+			 * Check the 3rd and 4th entries -
+			 * these sometimes contain random garbage
+			 */
+			start_sect = get_word (&p->start_sect);
+			if (i >= 2
+				&& start_sect + nr_sects > this_size
+				&& (this_sector + start_sect < first_sector ||
+				    this_sector + start_sect + first_sector >
+				     first_sector + first_size))
+				continue;
+
+			add_partition(hd, current_minor, this_sector+start_sect, nr_sects);
+			current_minor++;
+			if ((current_minor & mask) == 0)
+				goto done;
+		}
+		/*
+		 * Next, process the (first) extended partition, if present/
+		 * (So far, there seems to be no reason to make
+		 *  extended_partition() recurse and allow a tree
+		 *  of extended partitions.)
+		 * It should be a link to the next logical partition.
+		 * Create a minor for this just long enough to get the next
+		 * partition table.  The minor will be reused for the next
+		 * data partition.
+		 */
+		p -= 4;
+		for (i=0; i<4; i++) {
+			nr_sects = get_word (&p->nr_sects);
+			if (nr_sects && is_extended_partition(p))
+				break;
+		}
+		
+		if (i == 4)
+			goto done;	/* nothing left to do */
+
+		start_sect = get_word (&p->start_sect);
+		hd->part[current_minor].nr_sects = nr_sects;
+		hd->part[current_minor].start_sect = first_sector + start_sect;
+		this_sector = first_sector + start_sect;
+		dev = MKDEV(hd->major, current_minor);
+		brelse(bh);
+	}
+done:
+	brelse(bh);
+}
+
+static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
+{
+	int i, minor = current_minor;
+	struct buffer_head *bh;
+	struct partition *p;
+	unsigned char *data;
+	int mask = (1 << hd->minor_shift) - 1;
+	unsigned long start_sect, nr_sects;
+#ifdef CONFIG_BLK_DEV_IDE
+	int tested_for_dm6 = 0;
+
+read_mbr:
+#endif
+	if (!(bh = bread(dev,0,1024))) {
+		printk(" unable to read partition table\n");
+		return -1;
+	}
+	data = bh->b_data;
+	bh->b_dirt = 0;		/* In some cases we modify the geometry    */
+	bh->b_uptodate = 0;	/*  of the drive (below), so ensure that   */ 
+	bh->b_req = 0;		/*  nobody else tries to re-use this data. */
+#ifdef CONFIG_BLK_DEV_IDE
+check_table:
+#endif
+	if (*(unsigned short *)  (0x1fe + data) != 0xAA55) {
+		brelse(bh);
+		return 0;
+	}
+	p = (struct partition *) (0x1be + data);
+
+#ifdef CONFIG_BLK_DEV_IDE
+	/*
+	 * Check for Disk Manager v6.0x (or EZ-DRIVE) with geometry translation
+	 */
+	if (!tested_for_dm6++) {	/* Only check for DM6 *once* */
+		extern int ide_xlate_1024(kdev_t, int, const char *);
+		/* check for various "disk managers" which do strange things */
+		if (p->sys_ind == EZD_PARTITION) {
+			/*
+			 * The remainder of the disk must be accessed using
+			 * a translated geometry that reduces the number of
+			 * apparant cylinders to less than 1024 if possible.
+			 *
+			 * ide_xlate_1024() will take care of the necessary
+			 * adjustments to fool fdisk/LILO and partition check.
+			 */
+			if (ide_xlate_1024(dev, -1, " [EZD]")) {
+				data += 512;
+				goto check_table;
+			}
+		} else if (p->sys_ind == DM6_PARTITION) {
+			/*
+			 * Everything on the disk is offset by 63 sectors,
+			 * including the "new" MBR with its own partition table,
+			 * and the remainder of the disk must be accessed using
+			 * a translated geometry that reduces the number of
+			 * apparant cylinders to less than 1024 if possible.
+			 *
+			 * ide_xlate_1024() will take care of the necessary
+			 * adjustments to fool fdisk/LILO and partition check.
+			 */
+			if (ide_xlate_1024(dev, 1, " [DM6:DDO]")) {
+				brelse(bh);
+				goto read_mbr;
+			}
+		} else {
+			/*
+			 * look for DM6 signature in MBR, courtesy of OnTrack
+			 */
+			unsigned int sig = *(unsigned short *)(data + 2);
+			if (sig <= 0x1ae && *(unsigned short *)(data + sig) = 0x55AA
+			    && (1 & *(unsigned char *)(data + sig + 2)))
+			    	ide_xlate_1024(dev, 0, " [DM6:MBR]");
+			else if (p->sys_ind == DM6_AUX1PARTITION
+			      || p->sys_ind == DM6_AUX3PARTITION)
+				ide_xlate_1024(dev, 0, " [DM6:AUX]");
+		}
+	}
+#endif /* CONFIG_BLK_DEV_IDE */
+
+	current_minor += 4;
+	for (i=1 ; i<=4 ; minor++, i++, p++) {
+		nr_sects = get_word (&p->nr_sects);
+		if(!nr_sects)
+			continue;
+		start_sect = get_word (&p->start_sect);
+		add_partition(hd, minor, first_sector+start_sect, nr_sects);
+		if (is_extended_partition(p)) {
+			printk(" <");
+			/*
+			 * If we are rereading the partition table, we need
+			 * to set the size of the partition so that we will
+			 * be able to bread the block containing the extended
+			 * partition info.
+			 */
+			hd->sizes[minor] = hd->part[minor].nr_sects >> (BLOCK_SIZE_BITS - 9);
+			extended_partition(hd, MKDEV(hd->major, minor));
+			printk(" >");
+			/*
+			 * prevent someone doing mkfs or mkswap on an
+			 * extended partition, but leave room for LILO
+			 */
+			if (hd->part[minor].nr_sects > 2)
+				hd->part[minor].nr_sects = 2;
+		}
+	}
+	/*
+	 * Check for old-style Disk Manager partition table
+	 */
+	if (*(unsigned short *) (data + 0xfc) == 0x55AA) {
+		p = (struct partition *) (0x1be + data);
+		for (i = 4 ; i < 16 ; i++, current_minor++) {
+			p--;
+			if ((current_minor & mask) == 0)
+				break;
+			nr_sects = get_word (&p->nr_sects);
+			start_sect = get_word (&p->start_sect);
+			if (!(start_sect && nr_sects))
+				continue;
+			add_partition(hd, current_minor, start_sect, nr_sects);
+		}
+	}
+	printk("\n");
+	brelse(bh);
+	return 1;
+}			 
+
+#endif /* CONFIG_MSDOS_PARTITION */
+
+/* 'ARM hard drives' minor allocation:
+ *   physical hd	= minor >> hd->minor_shift
+ *   image file		= (minor-1) & 3
+ *   extra file		= image file + n<<2
+ */
+
+extern int image_allocate_list(int dev,int minor,struct hd_struct *);
+extern void image_release_dev(int dev);
+
+static void pc_check_hd(struct gendisk *hd, int minor)
+{ /* minor is the whole device minor */
+	int dev = (hd->major << 8)+minor;
+	int i;
+	struct buffer_head *bh;
+	struct partition *p;
+	
+	minor+=4;
+#define GET_WORD(n) (((unsigned char *)&n)[0] + (((unsigned char *)&n)[1] << 8) + \
+			(((unsigned char *)&n)[2] << 16) + (((unsigned char *)&n)[3] << 24))
+
+	if (!(bh = bread(dev,0,1024))) {
+		printk("  unable to read partition table of device %04x\n",dev);
+		return;
+	}
+	if (*(unsigned short *) (bh->b_data + 510) == 0xAA55) {
+		printk(" <");
+		p = (struct partition *) (0x1BE + bh->b_data);
+		for (i=1 ; i<=4 ; minor+=4,i++,p++) {
+			if (!(hd->part[minor].nr_sects = GET_WORD(p->nr_sects)))
+				continue;
+			hd->part[minor].start_sect = GET_WORD(p->start_sect);
+			print_minor_name (hd, minor);
+		}
+		printk(" >");
+	}
+#undef GET_WORD
+}
+
+static int arm_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
+{
+	int i, minor = current_minor;
+
+	current_minor += 4;
+	
+	for (i = 1; i <= 4; minor++, i++) {
+		int len;
+		len = image_allocate_list (dev, minor, &hd->part[minor]);
+		if (len) {
+			print_minor_name(hd, minor);
+			pc_check_hd(hd, minor);
+		}
+	}
+	printk("\n");
+	image_release_dev (dev);
+	return 1;
+}
+
+static void check_partition(struct gendisk *hd, kdev_t dev)
+{
+	static int first_time = 1;
+	unsigned long first_sector;
+	struct buffer_head *bh;
+
+	if (first_time)
+		printk("Partition check:\n");
+	first_time = 0;
+	first_sector = hd->part[MINOR(dev)].start_sect;
+
+	/*
+	 * This is a kludge to allow the partition check to be
+	 * skipped for specific drives (e.g. IDE cd-rom drives)
+	 */
+	if ((int)first_sector == -1) {
+		hd->part[MINOR(dev)].start_sect = 0;
+		return;
+	}
+
+	/*
+	 * kludge - read a k of data from the drive so that hd.c can
+	 * print its bits
+	 */
+	bh = bread (dev, 0, 1024);
+	brelse (bh);
+
+	printk(" ");
+	print_minor_name(hd, MINOR(dev));
+
+#ifdef CONFIG_MSDOS_PARTITION
+	if (msdos_partition(hd, dev, first_sector))
+		return;
+#endif
+	if (arm_partition(hd, dev, first_sector))
+		return;
+
+	printk(" unknown partition table\n");
+}
+
+/* This function is used to re-read partition tables for removable disks.
+   Much of the cleanup from the old partition tables should have already been
+   done */
+
+/* This function will re-read the partition tables for a given device,
+and set things back up again.  There are some important caveats,
+however.  You must ensure that no one is using the device, and no one
+can start using the device while this function is being executed. */
+
+void resetup_one_dev(struct gendisk *dev, int drive)
+{
+	int i;
+	int first_minor = drive << dev->minor_shift;
+	int end_minor   = first_minor + dev->max_p;
+
+	blk_size[dev->major] = NULL;
+	current_minor = 1 + first_minor;
+	check_partition(dev, MKDEV(dev->major, first_minor));
+
+	/*
+	 * We need to set the sizes array before we will be able to access
+	 * any of the partitions on this device.
+	 */
+	if (dev->sizes != NULL) {	/* optional safeguard in ll_rw_blk.c */
+		for (i = first_minor; i < end_minor; i++)
+			dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+		blk_size[dev->major] = dev->sizes;
+	}
+}
+
+static void setup_dev(struct gendisk *dev)
+{
+	int i, drive;
+	int end_minor	= dev->max_nr * dev->max_p;
+
+	blk_size[dev->major] = NULL;
+	for (i = 0 ; i < end_minor; i++)  {
+		dev->part[i].start_sect = 0;
+		dev->part[i].nr_sects = 0;
+	}
+	dev->init(dev);
+	for (drive=0 ; drive < dev->nr_real ; drive++) {
+		int first_minor = drive << dev->minor_shift;
+		current_minor = 1 + first_minor;
+		check_partition(dev, MKDEV(dev->major, first_minor));
+	}
+	if (dev->sizes != NULL) {	/* optional safeguard in ll_rw_blk.c */
+		for (i = 0 ; i < end_minor; i++)
+			dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+		blk_size[dev->major] = dev->sizes;
+	}
+}
+
+void device_setup(void)
+{
+	extern void console_map_init(void);
+	struct gendisk *p;
+	int nr=0;
+
+	chr_dev_init();
+	blk_dev_init();
+	sti();
+#ifdef CONFIG_SCSI
+	scsi_dev_init();
+#endif
+#ifdef CONFIG_INET
+	net_dev_init();
+#endif
+	console_map_init();
+
+	for (p = gendisk_head ; p ; p=p->next) {
+		setup_dev(p);
+		nr += p->nr_real;
+	}
+
+	if (ramdisk_size)
+		rd_load();
+}
diff -urNwbB linux/arch/arm/drivers/block/hd.c linux.arm/arch/arm/drivers/block/hd.c
--- linux/arch/arm/drivers/block/hd.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/hd.c	Sun Mar  3 12:24:12 1996
@@ -0,0 +1,1238 @@
+/*
+ *  linux/arch/arm/drivers/block/hd.c
+ *   [ origional file: linux/drivers/block/hd.c ]
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Modified 1995  Russell King for ARM.
+ */
+
+/*
+ * This is the low-level hd interrupt support. It traverses the
+ * request-list, using interrupts to jump between functions. As
+ * all the functions are called within interrupts, we may not
+ * sleep. Special care is recommended.
+ * 
+ *  modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ *
+ *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ *  in the early extended-partition checks and added DM partitions
+ *
+ *  IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ *  and general streamlining by mlord@bnr.ca (Mark Lord).
+ *
+ * Majorly hacked request routines to support image files. (RMK)
+ *  I am quite confident about these changes for the ARM, so long as
+ *  Multimode is not turned on...
+ */
+
+/* This is the maximum number of fragments accessed in one go */
+int no_hds;
+#define MAX_FRAGS	16
+static unsigned long frag_start[MAX_FRAGS];	/* Start of fragments */
+static unsigned long frag_len[MAX_FRAGS];	/* Fragment length */
+static int frag_count;				/* Number of fragments to go */
+static int frag_pos;				/* Position in fragment arrays */
+static unsigned int frag_sectors;
+
+#define DEFAULT_MULT_COUNT  0	/* set to 0 to disable multiple mode at boot */
+#define DEFAULT_UNMASK_INTR 0	/* set to 0 to *NOT* unmask irq's more often */
+
+#include <asm/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/config.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR HD_MAJOR
+#include "blk.h"
+
+void reissue_request(struct request *current_req);
+int issue_request(int dev, unsigned int block, unsigned int nsect, struct request *current_req);
+#undef  HD_IRQ
+#define HD_IRQ 11
+
+static int revalidate_hddisk(int, int);
+
+#define	HD_DELAY	0
+
+#define MAX_ERRORS     16	/* Max read/write errors/sector */
+#define RESET_FREQ      8	/* Reset controller every 8th retry */
+#define RECAL_FREQ      4	/* Recalibrate every 4th retry */
+#define MAX_HD		2
+
+#define STAT_OK		(READY_STAT|SEEK_STAT)
+#define OK_STATUS(s)	(((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
+
+static void recal_intr(void);
+static void bad_rw_intr(void);
+
+static char recalibrate[MAX_HD] = { 0, };
+static char special_op[MAX_HD] = { 0, };
+static int access_count[MAX_HD] = {0, };
+static char busy[MAX_HD] = {0, };
+static struct wait_queue * busy_wait = NULL;
+
+static int reset = 0;
+static int hd_error = 0;
+
+/*
+ *  This struct defines the HD's and their types.
+ */
+struct hd_i_struct {
+	unsigned int head,sect,cyl,wpcom,lzone,ctl;
+	};
+static struct hd_driveid *hd_ident_info[MAX_HD] = {0, };
+	
+#ifdef HD_TYPE
+static struct hd_i_struct hd_info[] = { HD_TYPE };
+struct hd_i_struct bios_info[] = { HD_TYPE };
+static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
+#else
+static struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
+struct hd_i_struct bios_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
+static int NR_HD = 0;
+#endif
+
+static struct hd_struct hd[MAX_HD<<6]={{0,0},};
+static int hd_sizes[MAX_HD<<6] = {0, };
+static int hd_blocksizes[MAX_HD<<6] = {0, };
+
+#if (HD_DELAY > 0)
+unsigned long last_req;
+
+unsigned long read_timer(void)
+{
+	unsigned long t, flags;
+	int i;
+
+	save_flags(flags);
+	cli();
+	t = jiffies * 11932;
+    	outb_p(0, 0x43);
+	i = inb_p(0x40);
+	i |= inb(0x40) << 8;
+	restore_flags(flags);
+	return(t - i);
+}
+#endif
+
+void hd_setup(char *str, int *ints)
+{
+	int hdind = 0;
+
+	if (ints[0] != 3)
+		return;
+	if (bios_info[0].head != 0)
+		hdind=1;
+	bios_info[hdind].head  = hd_info[hdind].head = ints[2];
+	bios_info[hdind].sect  = hd_info[hdind].sect = ints[3];
+	bios_info[hdind].cyl   = hd_info[hdind].cyl = ints[1];
+	bios_info[hdind].wpcom = hd_info[hdind].wpcom = 0;
+	bios_info[hdind].lzone = hd_info[hdind].lzone = ints[1];
+	bios_info[hdind].ctl   = hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+	NR_HD = hdind+1;
+}
+
+static void dump_status (char *msg, unsigned int stat)
+{
+	unsigned long flags;
+	char devc;
+
+	devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
+	save_flags (flags);
+	sti();
+	printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
+	if (stat & BUSY_STAT)	printk("Busy ");
+	if (stat & READY_STAT)	printk("DriveReady ");
+	if (stat & WRERR_STAT)	printk("WriteFault ");
+	if (stat & SEEK_STAT)	printk("SeekComplete ");
+	if (stat & DRQ_STAT)	printk("DataRequest ");
+	if (stat & ECC_STAT)	printk("CorrectedError ");
+	if (stat & INDEX_STAT)	printk("Index ");
+	if (stat & ERR_STAT)	printk("Error ");
+	printk("}\n");
+	if ((stat & ERR_STAT) == 0) {
+		hd_error = 0;
+	} else {
+		hd_error = inb(HD_ERROR);
+		printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
+		if (hd_error & BBD_ERR)		printk("BadSector ");
+		if (hd_error & ECC_ERR)		printk("UncorrectableError ");
+		if (hd_error & ID_ERR)		printk("SectorIdNotFound ");
+		if (hd_error & ABRT_ERR)	printk("DriveStatusError ");
+		if (hd_error & TRK0_ERR)	printk("TrackZeroNotFound ");
+		if (hd_error & MARK_ERR)	printk("AddrMarkNotFound ");
+		printk("}");
+		if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+			printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
+				inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
+			if (CURRENT)
+				printk(", sector=%ld", CURRENT->sector);
+		}
+		printk("\n");
+	}
+	restore_flags (flags);
+}
+
+void check_status(void)
+{
+	int i = inb_p(HD_STATUS);
+
+	if (!OK_STATUS(i)) {
+		dump_status("check_status", i);
+		bad_rw_intr();
+	}
+}
+
+static int controller_busy(void)
+{
+	int retries = 100000;
+	unsigned char status;
+
+	do {
+		status = inb_p(HD_STATUS);
+	} while ((status & BUSY_STAT) && --retries);
+	return status;
+}
+
+static int status_ok(void)
+{
+	unsigned char status = inb_p(HD_STATUS);
+
+	if (status & BUSY_STAT)
+		return 1;	/* Ancient, but does it make sense??? */
+	if (status & WRERR_STAT)
+		return 0;
+	if (!(status & READY_STAT))
+		return 0;
+	if (!(status & SEEK_STAT))
+		return 0;
+	return 1;
+}
+
+static int controller_ready(unsigned int drive, unsigned int head)
+{
+	int retry = 100;
+
+	do {
+		if (controller_busy() & BUSY_STAT)
+			return 0;
+		outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
+		if (status_ok())
+			return 1;
+	} while (--retry);
+	return 0;
+}
+
+static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
+		unsigned int head,unsigned int cyl,unsigned int cmd,
+		void (*intr_addr)(void))
+{
+	unsigned short port;
+
+#if (HD_DELAY > 0)
+	while (read_timer() - last_req < HD_DELAY)
+		/* nothing */;
+#endif
+	if (reset)
+		return;
+	if (!controller_ready(drive, head)) {
+		reset = 1;
+		return;
+	}
+	SET_INTR(intr_addr);
+	outb_p(hd_info[drive].ctl,HD_CMD);
+	port=HD_DATA;
+	outb_p(hd_info[drive].wpcom>>2,++port);
+	outb_p(nsect,++port);
+	outb_p(sect,++port);
+	outb_p(cyl,++port);
+	outb_p(cyl>>8,++port);
+	outb_p(0xA0|(drive<<4)|head,++port);
+	outb_p(cmd,++port);
+}
+
+static void hd_request (void);
+static unsigned int identified  [MAX_HD] = {0,}; /* 1 = drive ID already displayed   */
+static unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O       */
+static unsigned int max_mult    [MAX_HD] = {0,}; /* max sectors for MultMode         */
+static unsigned int mult_req    [MAX_HD] = {0,}; /* requested MultMode count         */
+static unsigned int mult_count  [MAX_HD] = {0,}; /* currently enabled MultMode count */
+static struct request WCURRENT;
+
+static void fixstring (unsigned char *s, int bytecount)
+{
+	unsigned char *p, *end = &s[bytecount &= ~1];	/* bytecount must be even */
+
+	/* convert from big-endian to little-endian */
+	for (p = end ; p != s;) {
+		unsigned short *pp = (unsigned short *) (p -= 2);
+		*pp = (*pp >> 8) | (*pp << 8);
+	}
+
+	/* strip leading blanks */
+	while (s != end && *s == ' ')
+		++s;
+
+	/* compress internal blanks and strip trailing blanks */
+	while (s != end && *s) {
+		if (*s++ != ' ' || (s != end && *s && *s != ' '))
+			*p++ = *(s-1);
+	}
+
+	/* wipe out trailing garbage */
+	while (p != end)
+		*p++ = '\0';
+}
+
+static void identify_intr(void)
+{
+	unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
+	unsigned short stat = inb_p(HD_STATUS);
+	struct hd_driveid *id = hd_ident_info[dev];
+
+	if (unmask_intr[dev])
+		sti();
+	if (stat & (BUSY_STAT|ERR_STAT)) {
+		printk ("  hd%c: non-IDE device, %dMB, CHS=%d/%d/%d\n", dev+'a',
+			hd_info[dev].cyl*hd_info[dev].head*hd_info[dev].sect / 2048,
+			hd_info[dev].cyl, hd_info[dev].head, hd_info[dev].sect);
+		if (id != NULL) {
+			hd_ident_info[dev] = NULL;
+			kfree_s (id, 512);
+		}
+	} else {
+		insw(HD_DATA, id, 256); /* get ID info */
+		max_mult[dev] = id->max_multsect;
+		if ((id->field_valid&1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
+			/*
+			 * Extract the physical drive geometry for our use.
+			 * Note that we purposely do *not* update the bios_info.
+			 * This way, programs that use it (like fdisk) will 
+			 * still have the same logical view as the BIOS does,
+			 * which keeps the partition table from being screwed.
+			 */
+			hd_info[dev].cyl  = id->cur_cyls;
+			hd_info[dev].head = id->cur_heads;
+			hd_info[dev].sect = id->cur_sectors; 
+		}
+		fixstring (id->serial_no, sizeof(id->serial_no));
+		fixstring (id->fw_rev, sizeof(id->fw_rev));
+		fixstring (id->model, sizeof(id->model));
+		printk ("  hd%c: %.40s, %dMB w/%dKB Cache, CHS=%d/%d/%d, MaxMult=%d\n",
+			dev+'a', id->model, id->cyls*id->heads*id->sectors/2048,
+			id->buf_size/2, hd_info[dev].cyl, hd_info[dev].head,
+			hd_info[dev].sect, id->max_multsect);
+		/*
+		 * Early model Quantum drives go weird at this point,
+		 *   but doing a recalibrate seems to "fix" them.
+		 * (Doing a full reset confuses some other model Quantums)
+		 */
+		if (!strncmp(id->model, "QUANTUM", 7))
+			special_op[dev] = recalibrate[dev] = 1;
+	}
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
+	hd_request();
+	return;
+}
+
+static void set_multmode_intr(void)
+{
+	unsigned int dev = DEVICE_NR(CURRENT->rq_dev), stat = inb_p(HD_STATUS);
+
+	if (unmask_intr[dev])
+		sti();
+	if (stat & (BUSY_STAT|ERR_STAT)) {
+		mult_req[dev] = mult_count[dev] = 0;
+		dump_status("set multmode failed", stat);
+	} else {
+		if ((mult_count[dev] = mult_req[dev]))
+			printk ("  hd%c: enabled %d-sector multiple mode\n",
+				dev+'a', mult_count[dev]);
+		else
+			printk ("  hd%c: disabled multiple mode\n", dev+'a');
+	}
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
+	hd_request();
+	return;
+}
+
+static int drive_busy(void)
+{
+	unsigned int i;
+	unsigned char c;
+
+	for (i = 0; i < 500000 ; i++) {
+		c = inb_p(HD_STATUS);
+		if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
+			return 0;
+	}
+	dump_status("reset timed out", c);
+	return 1;
+}
+
+static void reset_controller(void)
+{
+	int	i;
+
+	outb_p(4,HD_CMD);
+	for(i = 0; i < 1000; i++) nop();
+	outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
+	for(i = 0; i < 1000; i++) nop();
+	if (drive_busy())
+		printk("hd: controller still busy\n");
+	else if ((hd_error = inb(HD_ERROR)) != 1)
+		printk("hd: controller reset failed: %02x\n",hd_error);
+}
+
+static void reset_hd(void)
+{
+	static int i;
+
+repeat:
+	if (reset) {
+		reset = 0;
+		i = -1;
+		reset_controller();
+	} else {
+		check_status();
+		if (reset)
+			goto repeat;
+	}
+	if (++i < NR_HD) {
+		special_op[i] = recalibrate[i] = 1;
+		if (unmask_intr[i]) {
+			unmask_intr[i] = DEFAULT_UNMASK_INTR;
+			printk("hd%c: reset irq-unmasking to %d\n",i+'a',
+				DEFAULT_UNMASK_INTR);
+		}
+		if (mult_req[i] || mult_count[i]) {
+			mult_count[i] = 0;
+			mult_req[i] = DEFAULT_MULT_COUNT;
+			printk("hd%c: reset multiple mode to %d\n",i+'a',
+				DEFAULT_MULT_COUNT);
+		}
+		hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
+			hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
+		if (reset)
+			goto repeat;
+	} else
+		hd_request();
+}
+
+/*
+ * Ok, don't know what to do with the unexpected interrupts: on some machines
+ * doing a reset and a retry seems to result in an eternal loop. Right now I
+ * ignore it, and just set the timeout.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ */
+void unexpected_hd_interrupt(void)
+{
+	unsigned int stat = inb_p(HD_STATUS);
+
+	if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
+		dump_status ("unexpected interrupt", stat);
+		SET_TIMER;
+	}
+}
+
+/*
+ * bad_rw_intr() now tries to be a bit smarter and does things
+ * according to the error returned by the controller.
+ * -Mika Liljeberg (liljeber@cs.Helsinki.FI)
+ */
+static void bad_rw_intr(void)
+{
+	int dev;
+
+	if (!CURRENT)
+		return;
+	dev = DEVICE_NR(CURRENT->rq_dev);
+	if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
+		end_request(0);
+		special_op[dev] = recalibrate[dev] = 1;
+	} else if (CURRENT->errors % RESET_FREQ == 0)
+		reset = 1;
+	else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
+		special_op[dev] = recalibrate[dev] = 1;
+	/* Otherwise just retry */
+}
+
+static inline int wait_DRQ(void)
+{
+	int retries = 100000, stat;
+
+	while (--retries > 0)
+		if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
+			return 0;
+	dump_status("wait_DRQ", stat);
+	return -1;
+}
+
+static void read_intr(void)
+{
+	unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
+	int i, retries = 100000, msect = mult_count[dev], nsect;
+
+	if (unmask_intr[dev])
+		sti();			/* permit other IRQs during xfer */
+	do {
+		i = (unsigned) inb_p(HD_STATUS);
+		if (i & BUSY_STAT)
+			continue;
+		if (!OK_STATUS(i))
+			break;
+		if (i & DRQ_STAT)
+			goto ok_to_read;
+	} while (--retries > 0);
+	dump_status("read_intr", i);
+	bad_rw_intr();
+	hd_request();
+	return;
+ok_to_read:
+	if (msect) {
+		if ((nsect = CURRENT->current_nr_sectors) > msect)
+			nsect = msect;
+		msect -= nsect;
+	} else
+		nsect = 1;
+	insw(HD_DATA,CURRENT->buffer,nsect<<8);
+	CURRENT->sector += nsect;
+	CURRENT->buffer += nsect<<9;
+	CURRENT->errors = 0;
+	i = (CURRENT->nr_sectors -= nsect);
+
+#ifdef DEBUG
+	printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=0x%08lx\n",
+		dev+'a', CURRENT->sector, CURRENT->sector+nsect,
+		CURRENT->nr_sectors, (unsigned long) CURRENT->buffer+(nsect<<9));
+#endif
+	if ((CURRENT->current_nr_sectors -= nsect) <= 0)
+		end_request(1);
+	if (i > 0) {
+		if (msect)
+			goto ok_to_read;
+		if(!(--frag_sectors))
+		 /* Next fragment req.d */
+			reissue_request(CURRENT);
+		else
+		{
+			SET_INTR(&read_intr);
+		}
+		return;
+	}
+	(void) inb_p(HD_STATUS);
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
+	if (CURRENT)
+		hd_request();
+	return;
+}
+
+static inline void multwrite (unsigned int dev)
+{
+	unsigned int mcount = mult_count[dev];
+
+	while (mcount--) {
+		outsw(HD_DATA,WCURRENT.buffer,256);
+		if (!--WCURRENT.nr_sectors)
+			return;
+		WCURRENT.buffer += 512;
+		if (!--WCURRENT.current_nr_sectors) {
+			WCURRENT.bh = WCURRENT.bh->b_reqnext;
+			if (WCURRENT.bh == NULL)
+				panic("buffer list corrupted\n");
+			WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
+			WCURRENT.buffer             = WCURRENT.bh->b_data;
+		}
+	}
+}
+
+static void multwrite_intr(void)
+{
+	int i;
+	unsigned int dev = DEVICE_NR(WCURRENT.rq_dev);
+
+	if (unmask_intr[dev])
+		sti();
+	if (OK_STATUS(i=inb_p(HD_STATUS))) {
+		if (i & DRQ_STAT) {
+			if (WCURRENT.nr_sectors) {
+				multwrite(dev);
+				SET_INTR(&multwrite_intr);
+				return;
+			}
+		} else {
+			if (!WCURRENT.nr_sectors) {	/* all done? */
+				for (i = CURRENT->nr_sectors; i > 0;){
+					i -= CURRENT->current_nr_sectors;
+					end_request(1);
+				}
+#if (HD_DELAY > 0)
+				last_req = read_timer();
+#endif
+				if (CURRENT)
+					hd_request();
+				return;
+			}
+		}
+	}
+	dump_status("multwrite_intr", i);
+	bad_rw_intr();
+	hd_request();
+}
+
+static void write_intr(void)
+{
+	int i;
+	int retries = 100000;
+
+	if (unmask_intr[DEVICE_NR(WCURRENT.rq_dev)])
+		sti();
+	do {
+		i = (unsigned) inb_p(HD_STATUS);
+		if (i & BUSY_STAT)
+			continue;
+		if (!OK_STATUS(i))
+			break;
+		if ((CURRENT->nr_sectors <= 1) || (frag_sectors <= 1) || (i & DRQ_STAT))
+			goto ok_to_write;
+	} while (--retries > 0);
+	dump_status("write_intr", i);
+	bad_rw_intr();
+	hd_request();
+	return;
+ok_to_write:
+	CURRENT->sector++;
+	i = --CURRENT->nr_sectors;
+	--CURRENT->current_nr_sectors;
+	CURRENT->buffer += 512;
+	if (!i || (CURRENT->bh && !SUBSECTOR(i)))
+		end_request(1);
+	if (i > 0) {
+		if(!(--frag_sectors))
+		 /* Next fragment req.d */
+			reissue_request(CURRENT);
+		else
+		{
+			SET_INTR(&write_intr);
+			outsw(HD_DATA,CURRENT->buffer,256);
+			sti();
+		}
+	} else {
+#if (HD_DELAY > 0)
+		last_req = read_timer();
+#endif
+		hd_request();
+	}
+	return;
+}
+
+static void recal_intr(void)
+{
+	check_status();
+#if (HD_DELAY > 0)
+	last_req = read_timer();
+#endif
+	hd_request();
+}
+
+/*
+ * This is another of the error-routines I don't know what to do with. The
+ * best idea seems to just set reset, and start all over again.
+ */
+static void hd_times_out(void)
+{
+	unsigned int dev;
+
+	DEVICE_INTR = NULL;
+	if (!CURRENT)
+		return;
+	disable_irq(HD_IRQ);
+	sti();
+	reset = 1;
+	dev = DEVICE_NR(CURRENT->rq_dev);
+	printk("hd%c: timeout\n", dev+'a');
+	if (++CURRENT->errors >= MAX_ERRORS) {
+#ifdef DEBUG
+		printk("hd%c: too many errors\n", dev+'a');
+#endif
+		end_request(0);
+	}
+	cli();
+	hd_request();
+	enable_irq(HD_IRQ);
+}
+
+int do_special_op (unsigned int dev)
+{
+	if (recalibrate[dev]) {
+		recalibrate[dev] = 0;
+		hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
+		return reset;
+	}
+	if (!identified[dev]) {
+		identified[dev]  = 1;
+		unmask_intr[dev] = DEFAULT_UNMASK_INTR;
+		mult_req[dev]    = DEFAULT_MULT_COUNT;
+		hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
+		return reset;
+	}
+	if (mult_req[dev] != mult_count[dev]) {
+		hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
+		return reset;
+	}
+	if (hd_info[dev].head > 16) {
+		printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
+		end_request(0);
+	}
+	special_op[dev] = 0;
+	return 1;
+}
+
+/*
+ * The driver enables interrupts as much as possible.  In order to do this,
+ * (a) the device-interrupt is disabled before entering hd_request(),
+ * and (b) the timeout-interrupt is disabled before the sti().
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O.  For devices which don't suffer from
+ * that problem (most don't), the unmask_intr[] flag can be set to unmask
+ * other interrupts during data/cmd transfers (by defining DEFAULT_UNMASK_INTR
+ * to 1, or by using "hdparm -u1 /dev/hd?" from the shell).
+ */
+static void hd_request(void)
+{
+	unsigned int dev, block, nsect;
+
+	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) return;
+	if (DEVICE_INTR)
+		return;
+repeat:
+	timer_active &= ~(1<<HD_TIMER);
+	sti();
+	INIT_REQUEST;
+	if (reset) {
+		cli();
+		reset_hd();
+		return;
+	}
+	dev = MINOR(CURRENT->rq_dev);
+	block = CURRENT->sector;
+	nsect = CURRENT->nr_sectors;
+	if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
+#ifdef DEBUG
+		if (dev >= (NR_HD<<6))
+			printk("hd: bad minor number: device=0x%04x\n", CURRENT->rq_dev);
+		else
+			printk("hd%c: bad access: block=%d, count=%d\n",
+				(dev>>6)+'a', block, nsect);
+#endif
+		end_request(0);
+		goto repeat;
+	}
+	block += hd[dev].start_sect;
+	if(dev & 0x3f)
+	{ /* map sectors block to block+nsect to frags, 
+	   */
+		if(!image_file_check(CURRENT->rq_dev,CURRENT->cmd))
+			goto repeat;
+		frag_count = image_file_map(CURRENT->rq_dev, block, nsect, MAX_FRAGS, 
+						frag_start, frag_len);
+		if(!frag_count)
+		{
+			goto repeat;
+		}
+		
+		block = frag_start[0];
+		frag_sectors = nsect = frag_len[0];
+		frag_pos = 0;
+	}
+	else
+	{
+		frag_pos = 0;
+		frag_count = 1;
+		frag_sectors = nsect;
+		if(CURRENT->cmd == WRITE)
+		{ /* Protect normal HD from writes */
+			printk("hd%d : attempted write on protected drive\n",dev);
+			end_request(0);
+			goto repeat;
+		}
+	}
+	if(issue_request(dev,block,nsect, CURRENT))
+		goto repeat;
+}
+
+void reissue_request(struct request *current_req)
+{
+	unsigned int dev, block, nsect;
+
+	dev = MINOR(current_req->rq_dev);
+	frag_pos++;
+	if(frag_pos >= MAX_FRAGS)
+	{
+		block = current_req->sector;
+		block += hd[dev].start_sect;
+		nsect = current_req->nr_sectors;
+		if(!image_file_check(current_req->rq_dev,current_req->cmd))
+		{
+			end_request(0);
+			hd_request();
+			return;
+		}
+		frag_count = image_file_map(current_req->rq_dev, block, nsect, MAX_FRAGS, 
+						frag_start, frag_len);
+		if(!frag_count)
+		{
+			end_request(0);
+			hd_request();
+			return;
+		}
+		
+		frag_pos = 0;
+        }		
+	frag_sectors = frag_len[frag_pos];
+repeat:
+	if(issue_request(dev, frag_start[frag_pos], frag_len[frag_pos], current_req))
+	{
+		printk("EEEEK - going round the loop\n");
+		goto repeat;
+	}
+}
+
+/* 
+ * This routine actually issues a request to the hard disk itself
+ */
+int issue_request(int dev, unsigned int block, unsigned int nsect, struct request *current_req)
+{
+	unsigned int sec, track, head, cyl;
+int d = dev;
+	dev >>= 6;
+	if (special_op[dev]) {
+		if (do_special_op(dev))
+			return 1;
+		return 0;
+	}
+	sec   = block % hd_info[dev].sect + 1;
+	track = block / hd_info[dev].sect;
+	head  = track % hd_info[dev].head;
+	cyl   = track / hd_info[dev].head;
+#ifdef DEBUG
+	printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
+		dev+'a', (current_req->cmd == READ)?"read":"writ",
+		cyl, head, sec, nsect, (unsigned long) current_req->buffer,current_req);
+#endif
+	frag_sectors = nsect;
+	if (!unmask_intr[dev])
+		cli();
+	if (current_req->cmd == READ) {
+		unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
+		hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
+		if (reset)
+			return 1;
+		return 0;
+	}
+	if (current_req->cmd == WRITE) {
+#if 1
+#if 0
+		if (mult_count[dev])
+			hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
+		else
+#endif
+			hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+		if (reset)
+			return 1;
+		if (wait_DRQ()) {
+			bad_rw_intr();
+			return 1;
+		}
+#if 0
+		if (mult_count[dev]) {
+			WCURRENT = *current;
+			multwrite(dev);
+		} else
+#endif
+			outsw(HD_DATA,current_req->buffer,256);
+if(d == 1 && block < 10) while(1);
+#else
+if(d == 1)
+		printk("write: buffer = %p (%08lX)\n", current_req->buffer, *(unsigned long *)current_req->buffer);
+{int i; for(i=0; i<0x003fffff; i++); }
+		end_request(0);
+		return 1;
+#endif
+		return 0;
+	}
+	panic("unknown hd-command");
+	return 0;
+}
+
+static void do_hd_request (void)
+{
+	disable_irq(HD_IRQ);
+	hd_request();
+	enable_irq(HD_IRQ);
+}
+
+static int hd_ioctl(struct inode * inode, struct file * file,
+	unsigned int cmd, unsigned long arg)
+{
+	struct hd_geometry *loc = (struct hd_geometry *) arg;
+	int dev, err;
+	unsigned long flags;
+
+	if ((!inode) || (!inode->i_rdev))
+		return -EINVAL;
+	dev = DEVICE_NR(inode->i_rdev);
+	if (dev >= NR_HD)
+		return -EINVAL;
+	switch (cmd) {
+		case HDIO_GETGEO:
+			if (!loc)  return -EINVAL;
+			err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+			if (err)
+				return err;
+			put_fs_byte(bios_info[dev].head,
+				(char *) &loc->heads);
+			put_fs_byte(bios_info[dev].sect,
+				(char *) &loc->sectors);
+			put_fs_word(bios_info[dev].cyl,
+				(short *) &loc->cylinders);
+			put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
+				(long *) &loc->start);
+			return 0;
+		case BLKRASET:
+			if(!suser())  return -EACCES;
+			if(arg > 0xff) return -EINVAL;
+			read_ahead[MAJOR(inode->i_rdev)] = arg;
+			return 0;
+		case BLKRAGET:
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			put_fs_long(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+			return 0;
+         	case BLKGETSIZE:   /* Return device size */
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+			return 0;
+		case BLKFLSBUF:
+			if(!suser())  return -EACCES;
+			fsync_dev(inode->i_rdev);
+			invalidate_buffers(inode->i_rdev);
+			return 0;
+
+		case BLKRRPART: /* Re-read partition tables */
+			return revalidate_hddisk(inode->i_rdev, 1);
+
+		case HDIO_SET_UNMASKINTR:
+			if (!suser()) return -EACCES;
+			if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F))
+				return -EINVAL;
+			unmask_intr[dev] = arg;
+			return 0;
+
+                case HDIO_GET_UNMASKINTR:
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			put_fs_long(unmask_intr[dev], (long *) arg);
+			return 0;
+
+                case HDIO_GET_MULTCOUNT:
+			if (!arg)  return -EINVAL;
+			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+			if (err)
+				return err;
+			put_fs_long(mult_count[dev], (long *) arg);
+			return 0;
+
+		case HDIO_SET_MULTCOUNT:
+			if (!suser()) return -EACCES;
+			if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
+			save_flags(flags);
+			cli();	/* a prior request might still be in progress */
+			if (arg > max_mult[dev])
+				err = -EINVAL;	/* out of range for device */
+			else if (mult_req[dev] != mult_count[dev]) {
+				special_op[dev] = 1;
+				err = -EBUSY;	/* busy, try again */
+			} else {
+				mult_req[dev] = arg;
+				special_op[dev] = 1;
+				err = 0;
+			}
+			restore_flags(flags);
+			return err;
+
+		case HDIO_GET_IDENTITY:
+			if (!arg)  return -EINVAL;
+			if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
+			if (hd_ident_info[dev] == NULL)  return -ENOMSG;
+			err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct hd_driveid));
+			if (err)
+				return err;
+			memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
+			return 0;
+
+		RO_IOCTLS(inode->i_rdev,arg);
+		default:
+			return -EINVAL;
+	}
+}
+
+static int hd_open(struct inode * inode, struct file * filp)
+{
+	int target;
+
+	target =  DEVICE_NR(inode->i_rdev);
+
+	if (target >= NR_HD)
+		return -ENODEV;
+
+	while (busy[target])
+		sleep_on(&busy_wait);
+	access_count[target]++;
+	return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static void hd_release(struct inode * inode, struct file * file)
+{
+        int target;
+	sync_dev(inode->i_rdev);
+
+	target =  DEVICE_NR(inode->i_rdev);
+	access_count[target]--;
+
+}
+
+static void hd_geninit(struct gendisk *);
+
+static struct gendisk hd_gendisk = {
+	MAJOR_NR,	/* Major number */	
+	"hd",		/* Major name */
+	6,		/* Bits to shift to get real from partition */
+	1 << 6,		/* Number of partitions per real */
+	MAX_HD,		/* maximum number of real */
+	hd_geninit,	/* init function */
+	hd,		/* hd struct */
+	hd_sizes,	/* block sizes */
+	0,		/* number */
+	(void *) bios_info,	/* internal */
+	NULL		/* next */
+};
+	
+static void hd_interrupt(int irq, struct pt_regs *regs)
+{
+	void (*handler)(void) = DEVICE_INTR;
+
+	DEVICE_INTR = NULL;
+	timer_active &= ~(1<<HD_TIMER);
+	if (!handler)
+		handler = unexpected_hd_interrupt;
+	handler();
+	sti();
+}
+
+
+void set_hdinfo(int dev,unsigned char secsptrack,unsigned char heads,
+                    unsigned long discsize,unsigned int secsize)
+{
+  dev=MINOR(dev);
+  if(hd_info[dev>>6].cyl==1)
+  {
+    hd_info[dev>>6].cyl=discsize/(secsptrack*heads*secsize);
+    hd_info[dev>>6].head=heads;
+    hd_info[dev>>6].wpcom=-1;
+    hd_info[dev>>6].ctl=8;
+    hd_info[dev>>6].lzone=hd_info[dev>>6].cyl-1;
+    hd_info[dev>>6].sect=secsptrack;
+  }
+  hd[dev].start_sect=0;
+  hd[dev].nr_sects=discsize/secsize;
+#if 0
+  printk("hd%c: %d cylinders, %d heads, %d sectors (%d total)\n",'a'+(dev>>6),
+    hd_info[dev>>6].cyl, heads, secsptrack, discsize);
+#endif
+}
+
+/*
+ * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
+ * means we run the IRQ-handler with interrupts disabled: this is bad for
+ * interrupt latency, but anything else has led to problems on some
+ * machines...
+ *
+ * We enable interrupts in some of the routines after making sure it's
+ * safe.
+ */
+static void hd_geninit(struct gendisk *dev)
+{
+	int drive, i;
+
+NR_HD = 0;
+	if (!NR_HD) {
+	/* Default settings */
+		for (drive=0 ; drive<2 ; drive++) {
+			bios_info[drive].cyl	= hd_info[drive].cyl	= 1;
+			bios_info[drive].head	= hd_info[drive].head	= 1;
+			bios_info[drive].wpcom	= hd_info[drive].wpcom	= -1;
+			bios_info[drive].ctl	= hd_info[drive].ctl	= 8;
+			bios_info[drive].lzone	= hd_info[drive].lzone	= 1;
+			bios_info[drive].sect	= hd_info[drive].sect	= 17;
+			if(hd_info[drive].cyl && NR_HD == drive)
+				NR_HD++;
+		}
+        }
+
+	NR_HD = no_hds;
+
+	i = NR_HD;
+	while (i-- > 0) {
+		/*
+		 * The newer E-IDE BIOSs handle drives larger than 1024
+		 * cylinders by increasing the number of logical heads
+		 * to keep the number of logical cylinders below the
+		 * sacred INT13 limit of 1024 (10 bits).  If that is
+		 * what's happening here, we'll find out and correct
+		 * it later when "identifying" the drive.
+		 */
+		hd[i<<6].nr_sects = bios_info[i].head *
+				bios_info[i].sect * bios_info[i].cyl;
+		hd_ident_info[i] = (struct hd_driveid *) kmalloc(512,GFP_KERNEL);
+		special_op[i] = 1;
+	}
+	if (NR_HD) {
+		if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd")) {
+			printk("hd: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
+			NR_HD = 0;
+		} else {
+			request_region(HD_DATA, 8, "hd");
+			request_region(HD_CMD, 1, "hd(cmd)");
+		}
+	}
+	hd_gendisk.nr_real = NR_HD;
+
+	for(i=0;i<(MAX_HD << 6);i++) hd_blocksizes[i] = 1024;
+	blksize_size[MAJOR_NR] = hd_blocksizes;
+}
+
+static struct file_operations hd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	hd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	hd_open,		/* open */
+	hd_release,		/* release */
+	block_fsync		/* fsync */
+};
+
+int hd_init(void)
+{
+	if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
+		printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
+		return -1;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8;		/* 8 sector (4kB) read-ahead */
+	hd_gendisk.next = gendisk_head;
+	gendisk_head = &hd_gendisk;
+	timer_table[HD_TIMER].fn = hd_times_out;
+	return 0;
+}
+
+#define DEVICE_BUSY busy[target]
+#define USAGE access_count[target]
+#define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl)
+/* We assume that the the bios parameters do not change, so the disk capacity
+   will not change */
+#undef MAYBE_REINIT
+#define GENDISK_STRUCT hd_gendisk
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed scsi disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0.  If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+static int revalidate_hddisk(int dev, int maxusage)
+{
+	int target, major;
+	struct gendisk * gdev;
+	int max_p;
+	int start;
+	int i;
+	unsigned long flags;
+
+	target =  DEVICE_NR(dev);
+	gdev = &GENDISK_STRUCT;
+
+	save_flags(flags);
+	cli();
+	if (DEVICE_BUSY || USAGE > maxusage) {
+		restore_flags(flags);
+		return -EBUSY;
+	};
+	DEVICE_BUSY = 1;
+	restore_flags(flags);
+
+	max_p = gdev->max_p;
+	start = target << gdev->minor_shift;
+	major = MAJOR_NR << 8;
+
+	for (i=max_p - 1; i >=0 ; i--) {
+		sync_dev(major | start | i);
+		invalidate_inodes(major | start | i);
+		invalidate_buffers(major | start | i);
+		gdev->part[start+i].start_sect = 0;
+		gdev->part[start+i].nr_sects = 0;
+	};
+
+#ifdef MAYBE_REINIT
+	MAYBE_REINIT;
+#endif
+
+	gdev->part[start].nr_sects = CAPACITY;
+	resetup_one_dev(gdev, target);
+
+	DEVICE_BUSY = 0;
+	wake_up(&busy_wait);
+	return 0;
+}
+
diff -urNwbB linux/arch/arm/drivers/block/hdsrch.c linux.arm/arch/arm/drivers/block/hdsrch.c
--- linux/arch/arm/drivers/block/hdsrch.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/hdsrch.c	Sun Mar  3 12:29:41 1996
@@ -0,0 +1,665 @@
+/*
+ * linux/arch/arm/drivers/block/hdsrch.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ *
+ * Find image files on ADFS hard drives.
+ *  This currently only supports new map, 77 entry directory hard disks... (E format).
+ */
+
+#include <linux/config.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/genhd.h>
+
+#define RECSIZE 60
+
+#define MAX_BLK_FRAGS 64
+
+extern void print_minor_name (struct gendisk *hd, int minor);
+
+struct boot_block
+{
+  unsigned char  log2secsize;
+  unsigned char  secspertrack;
+  unsigned char  heads;
+  unsigned char  density;
+  unsigned char  idlen;
+  unsigned char  log2bpmb;
+  unsigned char  shew;
+  unsigned char  bootoption;
+  unsigned char  lowsector;
+  unsigned char  nzones;
+  unsigned short zone_spare;
+  unsigned long  root;
+  unsigned long  disc_size;
+  unsigned short disc_id;
+  unsigned char  disc_name[32-22];
+  unsigned long  disctype;
+};
+
+static struct boot_block *boot=NULL;
+static unsigned int zonesize;
+static unsigned int idsperzone;
+static long rootaddr;
+static long mapaddr;
+static long maplen;
+static void *map=NULL;
+static char ignoring;
+
+static int read_sectors(int dev,void *ptr,long address,int size)
+{
+  struct buffer_head *bh;
+  char *p=ptr;
+  while(size>0) {
+    if((bh=bread(dev,address/1024,1024))) {
+      memcpy(p,bh->b_data+((address & 0x200)?512:0),(address & 0x200 || size<1024)?512:1024);
+      if(address & 0x200) {
+      	p -= 512;
+        address -= 512;
+        size += 512;
+      }
+    } else {
+      printk("hda%c: bread failed\n",'a'+((dev & 0xc0)>>6));
+      return 0;
+    }
+    brelse(bh);
+    p += 1024;
+    address += 1024;
+    size -= 1024;
+  }
+  return 1;
+}
+
+/* + Read boot block at disc address &C00 (sector 6)
+ * + Calculate map address & length
+ */
+
+static int getdiskinfo(int dev)
+{
+  char *ptr;
+  extern void set_hdinfo(int,unsigned char,unsigned char,unsigned long,unsigned int);
+
+  if(boot)
+  	return 1;
+
+  boot=(struct boot_block *)kmalloc(sizeof(struct boot_block),GFP_KERNEL);
+  if(!boot) {
+    printk("hda%c: out of memory for bootblock\n",(dev>>6)+'a');
+    return 0;
+  }
+
+  ptr=(char *)kmalloc(1024,GFP_KERNEL);
+  if(!ptr) {
+    printk("hda%c: out of memory for bootblock\n",(dev>>6)+'a');
+    return 0;
+  }
+
+  if(!read_sectors(dev,ptr,0xC00,1024)) {
+    kfree_s(ptr,1024);
+    return 0;
+  }
+  memcpy(boot,(ptr+0x1C0),sizeof(struct boot_block));
+  kfree_s(ptr,1024);
+
+  set_hdinfo(dev,boot->secspertrack,boot->heads,boot->disc_size,1<<boot->log2secsize);
+
+  zonesize=(8<<boot->log2secsize)-boot->zone_spare;
+  idsperzone=zonesize/(boot->idlen+1);
+  mapaddr=((boot->nzones>>1)*zonesize-((boot->nzones>1)?RECSIZE*8:0))<<boot->log2bpmb;
+  maplen=boot->nzones<<boot->log2secsize;
+  return 1;
+}
+
+static unsigned char map_cross_valid_byte(void)
+{
+  unsigned char const *const map_base=map;
+  unsigned int check=0;
+  unsigned int i;
+
+  for(i=0;i<boot->nzones;i++)
+    check^=map_base[(i<<boot->log2secsize)+3];
+
+  return check;
+}
+
+static int get_map(int dev)
+{
+  if(!getdiskinfo(dev))
+    return 0;
+
+  if(map)
+    return 1;
+
+  map = (unsigned long *)vmalloc(maplen);
+  if(!map)
+    return 0;
+
+  if(!read_sectors(dev,map,mapaddr,maplen))
+    return 0;
+  if(map_cross_valid_byte()!=0xFF) {
+    printk("Map 1 has invalid cross check - trying second map\n");
+    if(!read_sectors(dev,map,mapaddr+maplen,maplen))
+      return 0;
+    if(map_cross_valid_byte()!=0xFF) {
+      printk("Map 2 has invalid cross check - ignoring drive\n");
+      ignoring=dev;
+      return 0;
+    }
+  }
+
+  memcpy(boot,(char*)map+4,sizeof(struct boot_block));
+
+  rootaddr=mapaddr+2*maplen;
+  return 1;
+}
+
+static int readmap(int sin,int *fzone,int *fstart,int *fend,int max_frags)
+{
+	static int startzone;	/* Start zone			 */
+	static int fragment;	/* Fragment id to look for	 */
+	static int curzone;	/* Current zone			 */
+	static int curmapp;	/* Current map bit position	 */
+  
+	register unsigned int t,z,frag,czone,cmap;
+	register unsigned char const *mapaddr;
+	register int *fz,*fs,*fe;
+	register unsigned char n;
+	
+  
+	if(sin) {/* If we have new address, start from new position */
+		fragment = sin >> 8;
+		curzone = startzone = fragment / idsperzone;
+		curmapp=0;
+	}
+	if(curzone == -1)
+		return 0;
+	frag = fragment;
+	czone = curzone;
+	cmap = curmapp;
+	fz = fzone;
+	fs = fstart;
+	fe = fend;
+
+/*   printk("readmap: internal_address: %p, fragment: %p, zone: %p\n",sin,frag, */
+/*     czone); */
+
+	while(1) {
+		mapaddr=(unsigned char const *)map+
+				(czone<<boot->log2secsize)+4+((czone==0)?RECSIZE:0);
+		while(cmap<zonesize-((czone==0)?RECSIZE*8:0))/* ? */ {
+			*fs=cmap;
+			z=cmap>>3;
+			t=((mapaddr[z]|(mapaddr[z+1]<<8)|(mapaddr[z+2]<<16))>>(cmap & 7)) &
+				((1<<boot->idlen)-1);
+			cmap+=boot->idlen;
+			
+			while((cmap & 7)!=0 && (mapaddr[cmap>>3] & (1<<(cmap & 7)))==0)
+				cmap+=1;
+
+			while((n=mapaddr[cmap>>3]) == 0)
+				cmap+=8;
+
+			while((n & (1<<(cmap & 7)))==0)
+				cmap+=1;
+			cmap+=1;
+			if(t==frag) {
+				*fz++=czone;
+				*fe++=cmap;
+				fs++;
+				if(!--max_frags) {
+/* 					printf("Breaking out\n"); */
+					curmapp = cmap;
+					curzone = czone;
+					return fs - fstart;
+				}
+			}
+		}
+		czone+=1;
+		if(czone==boot->nzones)
+			czone=0;
+		if(czone==startzone)
+			break;
+		cmap=0;
+	}
+	curzone = -1;
+	return fs - fstart;
+}
+
+static unsigned long calcaddr(int zone,int offset)
+{
+  if(zone)
+    return (zone*zonesize-RECSIZE*8+offset)<<boot->log2bpmb;
+  else
+    return offset<<boot->log2bpmb;
+}
+
+static void scan_name(char *name,int len)
+{
+  int i;
+  for(i=0;i<len+1;i++) {
+    if(name[i]<32 || name[i]=='.' || i==len) {
+      name[i]=0;
+      break;
+    }
+    if(islower(name[i]))
+      name[i]=toupper(name[i]);
+  }
+}
+
+static unsigned long scandir(int dev,unsigned long diraddress,char *file,int *type,
+       unsigned long *length)
+{
+  unsigned char *dir;
+  unsigned char *dd;
+  unsigned long addr;
+
+  scan_name(file,10);
+
+  dir=(unsigned char *)kmalloc(2048,GFP_KERNEL);
+
+  read_sectors(dev,dir,diraddress,2048);
+
+  if(strncmp((char*)dir+1,"Nick",4)!=0 && strncmp((char*)dir+1,"Hugo",4)!=0) {
+    printk("Broken directory - skipping\n");
+    return 0;
+  }
+
+  dd=dir+5-26;
+  do {
+    dd+=26;
+    scan_name((char*)dd,10);
+    if(strcmp(file,(char*)dd)==0)
+      break;
+  }
+  while(*dd!=0);
+  if(*dd==0)
+    return 0;
+  else
+  if(dd[25] & 8)
+    *type=0;
+  *length=(unsigned long)dd[18]|(dd[19]<<8)|(dd[20]<<16)|(dd[21]<<24);
+  addr=(unsigned long)dd[22]|(dd[23]<<8)|(dd[24]<<16);
+
+  kfree_s(dir,2048);
+
+  return addr;
+}
+
+static int search_path(int dev,char *path,unsigned long *start,unsigned long *length, int maxbits)
+{
+  unsigned long dir,iad,len;
+  int type,i,n,fzones[MAX_BLK_FRAGS],fstart[MAX_BLK_FRAGS],fend[MAX_BLK_FRAGS];
+  char *p;
+
+  if(!get_map(dev))
+    return 0;
+
+  dir=rootaddr;
+  while(path) {
+    type=1;
+    p=strchr(path,'.');
+    if(p!=NULL) {
+      p[0]=0;
+    }
+    if(path[0]!='\0') {
+      iad=scandir(dev,dir,path,&type,&len);
+      if(iad==0) {
+        printk("%s '%s' not found - skipping\n",p==NULL?"File":"Directory",path);
+        return 0;
+      }
+      if(type==0) {
+        n=readmap(iad,fzones,fstart,fend,1);
+        dir=calcaddr(fzones[0],fstart[0]);
+      } else
+      if(p!=NULL) {
+        printk("'%s' is a directory - skipping\n",path);
+        return 0;
+      } else {
+        int j=0;
+        unsigned int secoff;
+        secoff=(iad & 0xFF)?(unsigned int)((iad & 0xFF)-1)<<boot->log2secsize:0;
+        len=((len-1)|((1<<boot->log2secsize)-1))+1;
+        n=readmap(iad,fzones,fstart,fend,MAX_BLK_FRAGS); /* Handle 64 entries at a time */
+        do {
+          for(i=0;i<n;i++) {
+            start[j]=calcaddr(fzones[i],fstart[i]);
+            length[j]=calcaddr(fzones[i],fend[i])-start[j];
+            if(secoff!=0) {
+              if(length[j]>secoff) {
+                start[j]+=secoff;
+                length[j]-=secoff;
+                secoff=0;
+              } else {
+                secoff-=length[j];
+                continue;
+              }
+            }
+        
+            if(j > 0 && start[j]==(start[j-1]+length[j-1])) {/* Combine parts */
+              len += length[j-1];
+              length[j-1]+=length[j];
+              j--;
+            }
+        
+            if(length[j]>len) {
+              length[j]=len;
+              j++;
+              break;
+            } else
+              len-=length[j];
+            j++;
+            if(j >= maxbits) {
+#ifdef NOT_YET_TESTED
+		unsigned long **sn,**ln;
+		sn=(unsigned long*)kmalloc(maxbits+BLK_INCREMENT,GFP_KERNEL);
+		ln=(unsigned long*)kmalloc(maxbits+BLK_INCREMENT,GFP_KERNEL);
+		if(maxbits && start && length) {
+		  memcpy(sn,start,maxbits);
+		  memcpy(ln,length,maxbits);
+		  kfree_s((void *)start, maxbits);
+		  kfree_s((void *)length, maxbits);
+		}
+		maxbits+=BLK_INCREMENT;
+#else
+            	printk("**** EEEK!!!! - Image file is in too many chunks!\n");
+            	return 0;
+#endif
+            }
+          }
+        }
+        while((n=readmap(0,fzones,fstart,fend,MAX_BLK_FRAGS))>0);
+        return j;
+      }
+    }
+    if(p) {
+      path=p+1;
+      p[0]='.';
+    } else
+      path=0;
+  }
+  printk("Pathname invalid?!?\n");
+  return 0;
+}
+
+#define MAX_HD_IMAGES	8
+#define TWO_HDS
+#define MAX_HD_BITS	128
+
+static int initialised[MAX_HD_IMAGES];
+static int maxblock[MAX_HD_IMAGES];
+static unsigned long startchk[MAX_HD_IMAGES],lengthchk[MAX_HD_IMAGES];
+#ifndef NOT_YET_TESTED
+static unsigned long startaddr[MAX_HD_IMAGES][MAX_HD_BITS],
+		    lengthaddr[MAX_HD_IMAGES][MAX_HD_BITS];
+#else
+static unsigned long *startaddr[MAX_HD_IMAGES],
+                    *lengthaddr[MAX_HD_IMAGES];
+#endif
+
+/* --------------------------------------------------------------------------------- */
+/*  Externally visible functions/variables
+ * --------------------------------------------------------------------------------- */
+
+char arc_hd_files[MAX_HD_IMAGES][128]=
+{
+  {0,},
+  {0,},
+  {0,},
+  {0,}
+};
+
+/*
+ * Get the sectors used by device 'dev'
+ */
+int image_allocate_list(int dev,int devno,struct hd_struct *part)
+{
+	unsigned long chk1=0,chk2=0;
+	int i,sects=0;
+
+#ifndef TWO_HDS
+	if(dev!=0x300)
+	{
+		printk("Can't cope with >1 hd!\n");
+		return 0;
+	}
+#else
+	if(dev!=0x300 && dev!=0x340)
+	{
+		printk("Can't cope with >2 hds! (device %02X:%02X)\n",dev>>8,dev & 255);
+		return 0;
+	}
+	if(dev == 0x340)
+	  devno=(devno & 0x3f) + 4;
+#endif
+
+	if(ignoring==dev)
+		return 0;
+
+	devno--;
+
+	if(devno > MAX_HD_IMAGES)
+		return 0;
+
+	if(!arc_hd_files[devno][0])
+		return 0;
+
+#ifndef NOT_YET_TESTED
+	maxblock[devno]=search_path(dev,arc_hd_files[devno],startaddr[devno],lengthaddr[devno],MAX_HD_BITS);
+#else
+	maxblock[devno]=search_path(dev,arc_hd_files[devno],startaddr[devno],lengthaddr[devno],0);
+#endif
+	if(maxblock[devno]>MAX_HD_BITS)
+		panic("image allocator: image file is too fragmented\n");
+	if(!maxblock[devno])
+		return 0;
+
+	for(i=0; i<maxblock[devno]; i++)
+	{
+		/* convert to blocks & calculate checksums */
+#define ADDR2BLOCK(x) ((x)>>9)
+		startaddr[devno][i]=ADDR2BLOCK(startaddr[devno][i]);
+		lengthaddr[devno][i]=ADDR2BLOCK(lengthaddr[devno][i]);
+		chk1^=startaddr[devno][i];
+		chk2^=lengthaddr[devno][i];
+		sects+=lengthaddr[devno][i];
+	}
+	startchk[devno]=chk1;
+	lengthchk[devno]=chk2;
+	initialised[devno]=1;
+	part->start_sect=0;
+	part->nr_sects=sects;
+
+#ifdef DEBUG
+	for(i=0;i<n;i++)
+		printk("%10X : %10X\n",start[devno][i],length[devno][i]);
+#endif
+	return 1;
+}
+
+/*
+ * This is called to release all the memory with this device
+ *
+ * This must be called before accessing a new device
+ */
+
+void image_release_dev(int dev)
+{
+  if(boot) {
+    kfree_s(boot,sizeof(struct boot_block));
+    boot=NULL;
+  }
+
+  if(map) {
+    vfree(map);
+    map = NULL;
+  }
+
+  ignoring=0;
+}
+
+/* 
+ * This macro maps a device major/minor to the internal image file number
+ */
+#ifndef TWO_HDS
+#define GET_DEV(dev) \
+	if((dev)<0x301 || (dev)>0x314) \
+	{ \
+		printk("Bad device %X passed to image_file_check\n",(dev)); \
+		return 0; \
+	} \
+	(dev)=((dev)-1) & 3;
+#else
+#define GET_DEV(dev) \
+	if(((dev) & 0x33F)<0x301 || ((dev) & 0x33F)>0x314) \
+	{ \
+		printk("Bad device %X passed to image_file_check\n",(dev)); \
+		return 0; \
+	} \
+	switch((dev) & 0xC0) \
+	{ \
+		case 0x00: (dev)=((dev) -1) & 3;     break;\
+		case 0x40: (dev)=(((dev) -1) & 3)+4; break;\
+	}
+#endif
+
+/* 
+ * This function is called to perform checks on the image file.
+ *  Checks made: Image file has been initialised, and
+ *     for writes, the checksums on the start/length data are correct.
+ */
+int image_file_check(int dev,int cmd)
+{
+	long chk1=0,chk2=0;
+	int i;
+
+	GET_DEV(dev);
+
+	if (!initialised[dev]) {
+		printk("hd%c%d: %s of uninitialised image file.\n",(dev>>6)+'a',dev & 0x3f,
+		  cmd==WRITE?"write":"read");
+		return 0;
+	}
+
+	if (cmd==WRITE) {
+		/*
+		 * Check the table! Just in case. We don't want to write to a part of the HD that
+		 * is not allocated to us! It might be the map or something!
+		 */
+		for(i=0; i<maxblock[dev]; i++) {
+		  	chk1^=startaddr[dev][i];
+		  	chk2^=lengthaddr[dev][i];
+		}
+		if(chk1!=startchk[dev] || chk2!=lengthchk[dev]) {
+			printk("hda%c%d: mapping tables corrupted on write.",
+				(dev>>6)+'a',dev & 0x3f);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ * Map an image file block to a physical device returns 0 on error,
+ * or the number of entries.  This is called quite often when accessing
+ * image files.
+ */
+int image_file_map(int dev,unsigned long reqblock,unsigned long reqlength,
+		    unsigned long max_reqs,
+                    unsigned long *block,unsigned long *length)
+{
+	int i,num=0;
+	unsigned long blk=reqblock,totlen=0;
+
+	GET_DEV(dev);
+
+	for(i=0; i<maxblock[dev]; i++)
+	{
+		long remainder;
+		totlen+=lengthaddr[dev][i];
+		if(reqblock >= lengthaddr[dev][i])
+		{
+			reqblock-=lengthaddr[dev][i];
+			continue;
+		}
+		block[num]=reqblock+startaddr[dev][i];
+		remainder=reqblock+reqlength-lengthaddr[dev][i];
+		if(remainder<=0)
+		{
+			length[num]=reqlength;
+			return num+1;
+		}
+		length[num]=lengthaddr[dev][i]-reqblock;
+		if(num+1 >= max_reqs)
+			return num+1;
+		reqblock=0;
+		reqlength=remainder;
+		num++;
+	}
+
+	printk("hd%c%d: attempted read for sector %ld past end if image file at %ld.\n",
+		(dev>>6)+'a',dev & 0x3f,blk,totlen);
+		/* Should never happen! */
+	return 0;
+}
+
+
+
+#if 0
+static inline int check_bb(unsigned char *ptr)
+{
+	unsigned int result;
+	
+	__asm__("
+	adds	%3, %2, %3
+	sub	%3, %3, #1
+	b	LC02
+LC01:	ldrb	%4, [%3, #-1]!
+	adc	%0, %0, %4
+	movs	%0, %0, lsl #24
+	mov	%0, %0, lsr #24
+LC02:	teqs	%2, %3
+	bne	LC01
+	" : "=r" (result) : "0" (0), "r" (ptr), "r" (512), "r" (256));
+	return result != ptr[511];
+}
+
+static struct bootblock *bb;
+
+int arm_partition (struct gendisk *hd, kdev_t dev, unsigned long first_sector, int minor)
+{
+    int i;
+    struct buffer_head *bh;
+
+    current_minor = minor;
+
+    if (!(bh = bread (dev, 3, 1024))) {
+	printk (" unable to read boot sectors\n");
+	return -1;
+    }
+
+    if (check_bb (bh->b_data))
+    {
+	brelse (bh);
+	return 0;
+    }
+
+    bb = (struct bootblock *)(bh->b_data + 0x1c0);
+
+    set_hdinfo(dev,bb->secspertrack,bb->heads,bb->disc_size,1<<bb->log2secsize);
+
+    zonesize   = (8 << bb->log2secsize) - bb->zone_spare;
+    idsperzone = zonesize / (bb->idlen + 1);
+    mapaddr    = ((bb->nzones >> 1) * zonesize - ((bb->nzones > 1)? RECSIZE*8 : 0)) << bb->log2bpmb;
+    maplen     = bb->nzones << bb->log2secsize;
+
+    
+
+    brelse (bh);
+    return 1;
+}
+#endif
+
diff -urNwbB linux/arch/arm/drivers/block/mfmhd.c linux.arm/arch/arm/drivers/block/mfmhd.c
--- linux/arch/arm/drivers/block/mfmhd.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/mfmhd.c	Sun Mar  3 12:30:30 1996
@@ -0,0 +1,778 @@
+/*
+ * linux/arch/arm/drivers/block/mfmhd.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ *
+ * MFM hard drive code [experimental]
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#include <linux/mm.h>
+
+#include <linux/xd.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/major.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/dma.h>
+
+#include <asm/ecard.h>
+
+#define MFM_DISK_MAJOR	13
+#undef  XT_DISK_MAJOR
+#define XT_DISK_MAJOR	-1
+#define MAJOR_NR	MFM_DISK_MAJOR
+#include "blk.h"
+
+XD_INFO mfm_info[XD_MAXDRIVES];
+
+#define DEBUG
+
+#define MFM_COMMAND (mfm_addr + 0)
+#define MFM_DATAOUT (mfm_addr + 1)
+#define MFM_STATUS  (mfm_addr + 8)
+#define MFM_DATAIN  (mfm_addr + 9)
+
+static void mfm_geninit (void);
+static int  mfm_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg);
+static int  mfm_open (struct inode *inode,struct file *file);
+static void mfm_release (struct inode *inode, struct file *file);
+static void mfm_interrupt_handler (int, struct pt_regs *);
+static void mfm_seek (void);
+static void mfm_unexpected_interrupt (void);
+static void mfm_request (void);
+static int  mfm_reread_partitions (int dev);
+static void mfm_request (void);
+
+#define mfm_init xd_init
+
+static struct hd_struct mfm[XD_MAXDRIVES << 6];
+static int mfm_sizes[XD_MAXDRIVES << 6], mfm_access[XD_MAXDRIVES] = { 0, 0 };
+static int mfm_blocksizes[XD_MAXDRIVES << 6];
+static unsigned char mfm_valid[XD_MAXDRIVES] = { -1, };
+static struct wait_queue *mfm_wait_open = NULL;
+
+static int mfm_irq;		/* Interrupt number */
+static int mfm_addr;		/* Controller address */
+static int mfm_drives = 0;	/* drives available */
+static int mfm_status = 0;	/* interrupt status */
+static char mfm_busy = 0;	/* mfm busy */
+static int *errors;
+
+static struct rawcmd
+{
+  unsigned int flags;
+  unsigned int dev;
+  unsigned int cylinder;
+  unsigned int head;
+  unsigned int sector;
+  unsigned int cmdtype;
+  unsigned int cmdcode;
+  unsigned char cmddata[16];
+  unsigned int cmdlen;
+} raw_cmd;
+
+#define NEED_SEEK 1
+unsigned char result[16];
+unsigned char resultlen;
+
+static struct cont
+{
+	void (*interrupt)(void);/* interrupt handler */
+	void (*error)(void);	/* error handler */
+	void (*redo)(void);	/* redo handler */
+	void (*done)(int st);	/* done handler */
+} *cont = NULL;
+
+static struct drvparam
+{
+	int cylinder;
+	unsigned int nocylinders;
+	unsigned int lowcurrentcylinder;
+	unsigned int precompcylinder;
+	unsigned char noheads;
+	unsigned char nosectors;
+	struct
+	{
+		char recal;
+		char report;
+		char abort;
+	} errors;
+} mfm_drive_param[8] =
+{
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } },
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } },
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } },
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } },
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } },
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } },
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } },
+	{ -1, 616, 616, 129, 4, 32, { 0,0,1 } }
+};
+
+#define MFM_DRV_PARAM mfm_drive_param[raw_cmd.dev]
+
+struct tq_struct mfm_tq =
+{ 0, 0, (void (*) (void *)) mfm_unexpected_interrupt, 0 };
+
+#define NO_TRACK -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
+
+
+/* ------------------------------------------------------------------------------------------ */
+/*
+ * From the HD63463 data sheet from Hitachi Ltd.
+ */
+
+#define CMD_ABT		0xF0		/* Abort */
+#define CMD_SPC		0xE8		/* Specify */
+#define CMD_TST		0xE0		/* Test */
+#define CMD_RCLB	0xC8		/* Recalibrate */
+#define CMD_SEK		0xC0		/* Seek */
+#define CMD_WFS		0xAB		/* Write Format Skew */
+#define CMD_WFM		0xA3		/* Write Format */
+#define CMD_MTB		0x90		/* Memory to buffer */
+#define CMD_CMPD	0x88		/* Compare data */
+#define CMD_WD		0x87		/* Write data */
+#define CMD_RED		0x70		/* Read erroneous data */
+#define CMD_RIS		0x68		/* Read ID skew */
+#define CMD_FID		0x61		/* Find ID */
+#define CMD_RID		0x60		/* Read ID */
+#define CMD_BTM		0x50		/* Buffer to memory */
+#define CMD_CKD		0x48		/* Check data */
+#define CMD_RD		0x40		/* Read data */
+#define CMD_OPBW	0x38		/* Open buffer write */
+#define CMD_OPBR	0x30		/* Open buffer read */
+#define CMD_CKV		0x28		/* Check drive */
+#define CMD_CKE		0x20		/* Check ECC */
+#define CMD_POD		0x18		/* Polling disable */
+#define CMD_POL		0x10		/* Polling enable */
+#define CMD_RCAL	0x08		/* Recall */
+
+#define STAT_BSY	0x8000		/* Busy */
+#define STAT_CPR	0x4000		/* Command Parameter Rejection */
+#define STAT_CED	0x2000		/* Command end */
+#define STAT_SED	0x1000		/* Seek end */
+#define STAT_DER	0x0800		/* Drive error */
+#define STAT_ABN	0x0400		/* Abnormal end */
+#define STAT_POL	0x0200		/* Polling */
+
+/* ------------------------------------------------------------------------------------------ */
+
+static struct gendisk mfm_gendisk = {
+	MAJOR_NR,	/* Major number */
+	"mfm",		/* Major name */
+	6,		/* Bits to shift to get real from partition */
+	1 << 6,		/* Number of partitions per real */
+	XD_MAXDRIVES,	/* maximum number of real */
+	mfm_geninit,	/* init function */
+	mfm,		/* hd struct */
+	mfm_sizes,	/* block sizes */
+	0,		/* number */
+	(void *)mfm_info,/* internal */
+	NULL		/* next */
+};
+
+static struct file_operations mfm_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	mfm_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	mfm_open,		/* open */
+	mfm_release,		/* release */
+	block_fsync		/* fsync */
+};
+
+static struct expansion_card *ecs;
+static int mfm_prods[] = { 0x000b };
+static int mfm_manus[] = { 0x0000 };
+
+unsigned long mfm_init (unsigned long mem_start, unsigned long mem_end)
+{
+	ecs = ecard_find(0, sizeof(mfm_prods), mfm_prods, mfm_manus);
+	if(!ecs)
+		return mem_start;
+
+	mfm_irq = ecs->irq;
+	mfm_addr= ((((int)ecs->r_podaddr) & 0x03eff000) + 0x2000)>>2;
+
+	ecard_claim(ecs);
+
+	printk("mfm: controller found at address %X, interrupt %d\n", mfm_addr, mfm_irq);
+
+	if(register_blkdev(MAJOR_NR, "mfm", &mfm_fops)) {
+		printk("mfm_init: unable to get major number %d\n", MAJOR_NR);
+		return mem_start;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahread */
+#ifndef MODULE
+	mfm_gendisk.next = gendisk_head;
+	gendisk_head = & mfm_gendisk;
+#endif
+
+	return mem_start;
+}
+
+static int mfm_initdrives (void)
+{
+	mfm_info[0].heads = 4;
+	mfm_info[0].cylinders = 615;
+	mfm_info[0].sectors = 32;
+	mfm_info[0].control = 0;
+	return 1;
+}
+
+static void mfm_geninit (void)
+{
+	int i;
+
+	mfm_drives = mfm_initdrives();
+	printk("mfm: detected %d hard drive%s\n", mfm_drives, mfm_drives == 1?"":"s");
+	for(i = 0; i < mfm_drives; i++)
+		printk("mfm%d: heads = %d, cylinders = %d, sectors = %d\n", i,
+			 mfm_info[i].heads, mfm_info[i].cylinders, mfm_info[i].sectors);
+
+	if(request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk"))
+		printk("mfm: unable to get IRQ%d\n", mfm_irq);
+
+	outw(0x80, mfm_addr + (0x1000 >> 2));
+
+	for(i = 0; i < mfm_drives; i++) {
+		mfm[i << 6].nr_sects = mfm_info[i].heads * mfm_info[i].cylinders *
+				mfm_info[i].sectors;
+		mfm[i << 6].start_sect = 0;
+		mfm_valid[i] = 1;
+	}
+	mfm_gendisk.nr_real = mfm_drives;
+
+	for(i=0; i<(XD_MAXDRIVES << 6); i++)
+		mfm_blocksizes[i] = 1024;
+	blksize_size[MAJOR_NR] = mfm_blocksizes;
+}
+
+static int mfm_open (struct inode *inode,struct file *file)
+{
+	int dev = DEVICE_NR(MINOR(inode->i_rdev));
+
+	if (dev < mfm_drives) {
+		while (!mfm_valid[dev])
+			sleep_on(&mfm_wait_open);
+
+		mfm_access[dev]++;
+
+		return (0);
+	}
+	else
+		return (-ENODEV);
+}
+
+static int mfm_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
+{
+	XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg;
+	int dev = DEVICE_NR(MINOR(inode->i_rdev)),err;
+
+	if (inode && (dev < mfm_drives))
+		switch (cmd) {
+			case HDIO_GETGEO:
+				if (arg) {
+					if ((err = verify_area(VERIFY_WRITE, geometry, sizeof(*geometry))))
+						return (err);
+					put_fs_byte(mfm_info[dev].heads,(char *) &geometry->heads);
+					put_fs_byte(mfm_info[dev].sectors,(char *) &geometry->sectors);
+					put_fs_word(mfm_info[dev].cylinders,(short *) &geometry->cylinders);
+					put_fs_long(mfm[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start);
+
+					return (0);
+				}
+				break;
+			case BLKRASET:
+			  if(!suser())  return -EACCES;
+			  if(!inode->i_rdev) return -EINVAL;
+			  if(arg > 0xff) return -EINVAL;
+			  read_ahead[MAJOR(inode->i_rdev)] = arg;
+			  return 0;
+			case BLKGETSIZE:
+				if (arg) {
+					if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long))))
+						return (err);
+					put_fs_long(mfm[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
+
+					return (0);
+				}
+				break;
+			case BLKFLSBUF:
+				if(!suser())  return -EACCES;
+				if(!inode->i_rdev) return -EINVAL;
+				fsync_dev(inode->i_rdev);
+				invalidate_buffers(inode->i_rdev);
+				return 0;
+
+			case BLKRRPART:
+				return (mfm_reread_partitions(inode->i_rdev));
+			RO_IOCTLS(inode->i_rdev,arg);
+		}
+	return (-EINVAL);
+}
+
+static void mfm_release (struct inode *inode, struct file *file)
+{
+	int dev = DEVICE_NR(MINOR(inode->i_rdev));
+
+	if (dev < mfm_drives) {
+		sync_dev(dev);
+		mfm_access[dev]--;
+	}
+}
+
+static int mfm_reread_partitions (int dev)
+{
+	int target = DEVICE_NR(MINOR(dev)),start = target << mfm_gendisk.minor_shift,partition;
+
+	cli();
+
+	mfm_valid[target] = (mfm_access[target] != 1);
+
+	sti();
+
+	if (mfm_valid[target])
+		return -EBUSY;
+
+	for (partition = mfm_gendisk.max_p - 1; partition >= 0; partition--) {
+		sync_dev(MAJOR_NR << 8 | start | partition);
+		invalidate_inodes(MAJOR_NR << 8 | start | partition);
+		invalidate_buffers(MAJOR_NR << 8 | start | partition);
+		mfm_gendisk.part[start + partition].start_sect = 0;
+		mfm_gendisk.part[start + partition].nr_sects = 0;
+	};
+
+	mfm_gendisk.part[start].nr_sects = mfm_info[target].heads *
+		mfm_info[target].cylinders * mfm_info[target].sectors;
+	resetup_one_dev(&mfm_gendisk,target);
+
+	mfm_valid[target] = 1;
+	wake_up(&mfm_wait_open);
+
+	return 0;
+}
+
+static void print_status (void)
+{
+	char *error;
+	static char *errors[] =
+	{ "no error",
+	  "command aborted",
+	  "invalid command",
+	  "parameter error",
+	  "not initialised",
+	  "rejected TEST",
+	  "no useld",
+	  "write fault",
+	  "not ready",
+	  "no scp",
+	  "in seek",
+	  "invalid NCA",
+	  "invalid step rate",
+	  "seek error",
+	  "over run",
+	  "invalid PHA",
+	  "data field EEC error",
+	  "data field CRC error",
+	  "error corrected",
+	  "data field fatal error",
+	  "no data am",
+	  "not hit",
+	  "ID field CRC error",
+	  "time over",
+	  "no ID am",
+	  "not writable"
+	};
+	if(result[1] < 0x65)
+		error = errors[result[1] >> 2];
+	else
+		error = "unknown";
+	printk("(");
+	if(mfm_status & STAT_BSY)
+		printk("BSY ");
+	if(mfm_status & STAT_CPR)
+		printk("CPR ");
+	if(mfm_status & STAT_CED)
+		printk("CED ");
+	if(mfm_status & STAT_SED)
+		printk("SED ");
+	if(mfm_status & STAT_DER)
+		printk("DER ");
+	if(mfm_status & STAT_ABN)
+		printk("ABN ");
+	if(mfm_status & STAT_POL)
+		printk("POL ");
+	printk(") SSB = %X (%s)\n", result[1], error);
+
+}
+
+/* ------------------------------------------------------------------------------------- */
+
+static void issue_command (int command, unsigned char *cmdb, int len)
+{
+	int status;
+#ifdef DEBUG
+	int i;
+	printk("issue_command: %02X: ",command);
+	for(i=0; i<len; i++)
+	  printk("%02X ", cmdb[i]);
+	printk("\n");
+#endif
+
+	do {
+		status = inw(MFM_STATUS);
+	} while(status & (STAT_BSY|STAT_POL));
+	if(status & STAT_CPR)
+	{
+		outw(CMD_RCAL, MFM_COMMAND);
+		while(inw(MFM_STATUS) & STAT_BSY);
+	}
+
+	while(len > 0)
+	{
+		outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT);
+		len -= 2;
+		cmdb += 2;
+	}
+	outw(command, MFM_COMMAND);
+}
+
+static void wait_for_completion (void)
+{
+	while((mfm_status = inw(MFM_STATUS)) & STAT_BSY);
+}
+
+/* ------------------------------------------------------------------------------------- */
+
+static void mfm_rw_intr (void)
+{
+	printk("mfm_rw_intr...\n");
+	if (cont)
+	{
+		cont->done(1);
+	}
+	mfm_request();
+}
+
+static void mfm_setup_rw (void)
+{
+	printk("setting up for rw...\n");
+#if 1
+	SET_INTR(mfm_rw_intr);
+	issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen);
+#else
+	if(cont)
+	{
+		cont->done(0);
+		cont->redo();
+	}
+#endif
+}
+
+static void mfm_recal_intr (void)
+{
+	printk("recal intr - status = ");
+	print_status();
+	if(mfm_status & (STAT_DER|STAT_ABN)) {
+		printk("recal failed\n");
+		MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
+		if(cont)
+		{
+			cont->error();
+			cont->redo();
+		}
+		return;
+	}
+	if(mfm_status & STAT_SED) {
+		issue_command(CMD_POD, NULL, 0);
+		MFM_DRV_PARAM.cylinder = 0;
+		mfm_seek();
+		return;
+	}
+	if(mfm_status & STAT_CED) {
+		SET_INTR(mfm_recal_intr);
+		issue_command(CMD_POL, NULL, 0);
+		return;
+	}
+	printk("recal: unknown status\n");
+}
+
+static void mfm_seek_intr (void)
+{
+	printk("seek intr - status = ");
+	print_status();
+	if(mfm_status & (STAT_DER|STAT_ABN)) {
+		printk("seek failed\n");
+		MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
+		if(cont)
+		{
+			cont->error();
+			cont->redo();
+		}
+		return;
+	}
+	if(mfm_status & STAT_SED) {
+		issue_command(CMD_POD, NULL, 0);
+		MFM_DRV_PARAM.cylinder = raw_cmd.cylinder;
+		mfm_seek();
+		return;
+	}
+	if(mfm_status & STAT_CED) {
+		SET_INTR(mfm_seek_intr);
+		issue_command(CMD_POL, NULL, 0);
+		return;
+	}
+	printk("seek: unknown status\n");
+}
+
+static void mfm_specify (void)
+{
+	unsigned char cmdb[16];
+	printk("specify...\n");
+	cmdb[0] = 0x1F;
+	cmdb[1] = 0xC3;
+	cmdb[2] = 0x16;
+	cmdb[3] = 0x02;
+	cmdb[4] = 0xFC | ((MFM_DRV_PARAM.nocylinders - 1) >> 8);
+	cmdb[5] = MFM_DRV_PARAM.nocylinders - 1;
+	cmdb[6] = MFM_DRV_PARAM.noheads - 1;
+	cmdb[7] = MFM_DRV_PARAM.nosectors - 1;
+	cmdb[8] = 0xA9;
+	cmdb[9] = 0x0A;
+	cmdb[10]= 0x0D;
+	cmdb[11]= 0x0C;
+	cmdb[12]= (MFM_DRV_PARAM.precompcylinder - 1) >> 8;
+	cmdb[13]= MFM_DRV_PARAM.precompcylinder - 1;
+	cmdb[14]= (MFM_DRV_PARAM.lowcurrentcylinder - 1) >> 8;
+	cmdb[15]= MFM_DRV_PARAM.lowcurrentcylinder - 1;
+
+	issue_command(CMD_SPC, cmdb, 16);
+	wait_for_completion();
+}
+
+static void mfm_seek (void)
+{
+	unsigned char cmdb[4];
+	printk("seeking...\n");
+	if(MFM_DRV_PARAM.cylinder < 0) {
+		cmdb[0] = raw_cmd.dev + 1;
+		cmdb[1] = raw_cmd.head;
+
+		SET_INTR(mfm_recal_intr);
+		issue_command(CMD_RCLB, cmdb, 2);
+		return;
+	}
+	mfm_specify();
+	if(MFM_DRV_PARAM.cylinder != raw_cmd.cylinder)
+	{
+		cmdb[0] = raw_cmd.dev + 1;
+		cmdb[1] = raw_cmd.head;
+		cmdb[2] = raw_cmd.cylinder >> 8;
+		cmdb[3] = raw_cmd.cylinder;
+
+		SET_INTR(mfm_seek_intr);
+		issue_command(CMD_SEK, cmdb, 4);
+	}
+	else
+		mfm_setup_rw();
+}
+
+static void mfm_initialise (void)
+{
+	printk("init...\n");
+	if(raw_cmd.flags & NEED_SEEK)
+		mfm_seek();
+	else
+		mfm_setup_rw();
+}
+
+/* ------------------------------------------------------------------------------------- */
+
+static void request_done(int uptodate)
+{
+	if(!CURRENT) {
+		printk("mfm: request list destroyed\n");
+		return;
+	}
+	if(uptodate) {
+		end_request (1);
+	}
+	else {
+		end_request (0);
+	}
+}
+
+static void error_handler (void)
+{
+	int i;
+	printk("error detected... status = ");
+	print_status();
+	(*errors) ++;
+	if(*errors > MFM_DRV_PARAM.errors.abort)
+		cont->done(0);
+	if(*errors > MFM_DRV_PARAM.errors.recal)
+		MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
+}
+
+static void rw_interrupt(void)
+{
+}
+
+static struct cont rw_cont ={
+	rw_interrupt,
+	error_handler,
+	mfm_request,
+	request_done
+};
+
+static void mfm_request (void)
+{
+	unsigned int dev, block, nsect, track;
+
+	if(CURRENT && CURRENT->dev < 0)
+		return;
+
+	while(1){
+		sti();
+
+		INIT_REQUEST;
+		dev = MINOR(CURRENT->dev);
+		block = CURRENT->sector;
+		nsect = CURRENT->nr_sectors;
+
+		if(dev >= (mfm_drives << 6) ||
+		    block >= mfm[dev].nr_sects || ((block+nsect) > mfm[dev].nr_sects)) {
+#ifdef DEBUG
+			if(dev >= (mfm_drives << 6))
+				printk("mfm: bad minor number: device=0x%04x\n", CURRENT->dev);
+			else
+				printk("mfm%c: bad access: block=%d, count=%d\n", (dev>>6)+'a',
+					block, nsect);
+#endif
+			end_request(0);
+			continue;
+		}
+		block += mfm[dev].start_sect;
+
+		if(CURRENT->cmd != READ && CURRENT->cmd != WRITE)
+		{
+			printk("unknown mfm-command %d\n", CURRENT->cmd);
+			end_request(0);
+			continue;
+		}
+
+		dev >>= 6;
+
+		track = block / mfm_info[dev].sectors;
+		raw_cmd.flags		= NEED_SEEK;
+		raw_cmd.dev		= dev;
+		raw_cmd.sector		= block % mfm_info[dev].sectors;
+		raw_cmd.head		= track % mfm_info[dev].heads;
+		raw_cmd.cylinder	= track / mfm_info[dev].heads;
+		raw_cmd.cmdtype 	= CURRENT->cmd;
+		raw_cmd.cmdcode 	= CURRENT->cmd == WRITE ? CMD_WD : CMD_RD;
+		raw_cmd.cmddata[0]	= dev;
+		raw_cmd.cmddata[1]	= raw_cmd.head;
+		raw_cmd.cmddata[2]	= raw_cmd.cylinder >> 8;
+		raw_cmd.cmddata[3]	= raw_cmd.cylinder;
+		raw_cmd.cmddata[4]	= raw_cmd.head;
+		raw_cmd.cmddata[5]	= raw_cmd.sector;
+		raw_cmd.cmddata[6]	= nsect >> 8;
+		raw_cmd.cmddata[7]	= nsect;
+		raw_cmd.cmdlen		= 8;
+
+#ifdef DEBUG
+		printk("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
+			raw_cmd.dev+'a', (CURRENT->cmd == READ)?"read":"writ",
+			raw_cmd.cylinder,
+			raw_cmd.head,
+			raw_cmd.sector, nsect, (unsigned long)CURRENT->buffer, CURRENT);
+#endif
+		cont = &rw_cont;
+		errors = &(CURRENT->errors);
+		mfm_tq.routine = (void (*)(void *))mfm_initialise;
+		queue_task(&mfm_tq, &tq_timer);
+		break;
+	}
+}
+
+static void do_mfm_request (void)
+{
+	mfm_request();
+}
+
+static void mfm_unexpected_interrupt (void)
+{
+	printk("mfm: unexpected interrupt - status = ");
+	print_status();
+}
+
+static void mfm_interrupt_handler (int unused, struct pt_regs *regs)
+{
+	int i;
+	void (*handler)(void) = DEVICE_INTR;
+
+	CLEAR_INTR;
+	mfm_status = inw(MFM_STATUS);
+
+	if((mfm_status & (STAT_CPR|STAT_BSY)) == STAT_CPR)
+	{
+		int len = 0;
+		while(len <16)
+		{
+			int in;
+			in = inw(MFM_DATAIN);
+			result[len++] = in>>8;
+			result[len++] = in;
+		}
+	}
+
+	outw(CMD_RCAL, MFM_COMMAND);	/* Clear interrupt condition */
+
+	if(!handler) {
+		mfm_unexpected_interrupt();
+		return;
+	}
+	mfm_tq.routine = (void (*)(void *))handler;
+	queue_task_irq(&mfm_tq, &tq_timer);
+}
+
+#ifdef MODULE
+
+char kernel_version[] = UTS_RELEASE;
+
+int init_module (void)
+{
+	mfm_init(0, 0);
+	mfm_geninit();
+	return 0;
+}
+
+void cleanup_module (void)
+{
+	if (ecs)
+		ecard_release(ecs);
+}
+	
+#endif
diff -urNwbB linux/arch/arm/drivers/char/Makefile linux.arm/arch/arm/drivers/char/Makefile
--- linux/arch/arm/drivers/char/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/Makefile	Sat Feb 24 14:18:57 1996
@@ -0,0 +1,87 @@
+#
+# Makefile for the kernel character device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+#
+# This file contains the font map for the default font
+#
+
+all: links first_rule
+
+FONTMAPFILE = cp437.uni
+
+L_TARGET := char.a
+M_OBJS   :=
+L_OBJS   := tty_io.o n_tty.o console.o keyboard.o serial.o \
+	tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
+	defkeymap.o consolemap.o iic.o
+
+# vesa_blank.o selection.o
+
+
+ifeq ($(CONFIG_PRINTER),y)
+  L_OBJS += lp.o
+else
+  ifeq ($(CONFIG_PRINTER),m)
+    M_OBJS += lp.o
+  endif
+endif
+
+ifeq ($(CONFIG_MOUSE),y)
+  M = y
+else
+  ifeq ($(CONFIG_MOUSE),m)
+    M = mm
+  endif
+endif
+
+ifeq ($(CONFIG_KBDMOUSE),y)
+  M = y
+  L_OBJS += kbdmouse.o
+else
+  ifeq ($(CONFIG_KBDMOUSE),m)
+    MM = m
+    M_OBJS += kbdmouse.o
+  endif
+endif
+
+ifdef M
+  L_OBJS += mouse.o
+else
+  ifdef MM
+    M_OBJS += mouse.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+fastdep: links uni_hash.tbl
+
+consolemap.o:
+
+conmakehash: conmakehash.c
+	$(HOSTCC) -o conmakehash conmakehash.c
+
+uni_hash.tbl: $(FONTMAPFILE) conmakehash
+	./conmakehash $(FONTMAPFILE) > uni_hash.tbl
+
+LK = conmakehash.c consolemap.c consolemap.h cp437.uni kbd_kern.h lp.c random.c
+
+links:
+	-@for f in $(LK); do \
+		ln -s ../../../../drivers/char/$$f .; \
+	done
+	touch links
+
+LINKCLEAN:
+	-@for f in $(LK); do \
+		if [ -L $$f ]; then rm -f $$f; fi; \
+	done
+	rm -f links
diff -urNwbB linux/arch/arm/drivers/char/console.c linux.arm/arch/arm/drivers/char/console.c
--- linux/arch/arm/drivers/char/console.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/console.c	Sun Mar  3 12:31:06 1996
@@ -0,0 +1,2722 @@
+/*
+ * linux/arch/arm/drivers/char/console.c
+ *
+ * Modifications (C) 1995, 1996 Russell King
+ */
+
+/*
+ * This module exports the console io functions:
+ *
+ *	'int vc_allocate(unsigned int console)'
+ *	'int vc_cons_allocated(unsigned int console)'
+ *	'int vc_resize(unsigned long lines,unsigned long cols)'
+ *	'void vc_disallocate(unsigned int currcons)'
+ *
+ *	'unsigned long con_init(unsigned long)'
+ * S	'int con_open(struct tty_struct *tty,struct file *filp)'
+ * S	'void con_write(struct tty_struct *tty)'
+ * S	'void console_print(const char *b)'
+ *	'void update_screen(int new_console)'
+ *
+ *	'void blank_screen(void)'
+ *	'void unblank_screen(void)'
+ *	'void poke_blanked_console(void)'
+ *	'void scrollback(int lines)'			*
+ *	'void scrollfront(int lines)'			*
+ *	'int do_screendump(int arg)'
+ *
+ *	'int con_get_font(char *)'
+ *	'int con_set_font(char *)'
+ *	'int con_get_trans(char *)'
+ *	'int con_set_trans(char *)'
+ *
+ *	'int set_selection(const int arg)'		*
+ *	'int paste_selection(struct tty_struct *tty)'	*
+ *	'int sel_loadlut(const int arg)'		*
+ *	'int mouse_reporting(void)'			*
+ *
+ */
+
+#define BLANK 0x0020
+
+/* A bitmap for codes <32. A bit of 1 indicates that the code
+ * corresponding to that bit number invokes a special action
+ * (such as cursor movement) and should not be displayed as a
+ * glyph unless the disp_ctrl mode is explicitly enabled.
+ */
+#define CTRL_ACTION 0x0d00ff81
+#define CTRL_ALWAYS 0x0800f501  /* Cannot be overriden by disp_ctrl */
+ 
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/kd.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/irq-no.h>
+
+#include "kbd_kern.h"
+#include "vt_kern.h"
+#include "consolemap.h"
+
+/* ARM Extensions */
+extern void memc_write (int reg, int val);
+extern void vidc_write (int reg, int val);
+extern void ll_char_write(unsigned long ps, int ch, unsigned char forec,
+			 unsigned char backc, unsigned char flgs);
+extern void memfastset (void *ptr, unsigned long word, int length);
+extern void prints (const char *, ...);
+extern void map_screen_mem (unsigned long vid_base, int remap);
+static void ll_erase(int currcons,unsigned char sx,unsigned char sy,
+			unsigned char cx,unsigned char cy);
+static void vsync_irq(int irq, struct pt_regs *regs);
+static void put_cursor(char on_off,unsigned long cpp);
+unsigned char bytes_per_char_h;
+unsigned char bytes_per_char_v;
+unsigned long video_addr_mask;
+/* MUST get rid of these */
+#define BOLD		0x01
+#define ITALIC		0x02
+#define UNDERLINE	0x04
+#define FLASH		0x08
+#define INVERSE		0x10
+#define FLAGS ((underline?UNDERLINE:0)|(reverse?INVERSE:0)|(blink?FLASH:0)|intensity)
+
+#include <linux/ctype.h>
+
+/* Routines for selection control. */
+int set_selection(const int arg, struct tty_struct *tty);
+int paste_selection(struct tty_struct *tty);
+static void clear_selection(void);
+static void highlight_pointer(const int currcons,const int where);
+
+#define SEL_BUFFER_SIZE 4096
+int sel_cons = 0;
+static int sel_start = -1;
+static int sel_end;
+static char sel_buffer[SEL_BUFFER_SIZE] = { '\0' };
+
+
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+struct tty_driver console_driver;
+static int console_refcount;
+static struct tty_struct *console_table[MAX_NR_CONSOLES];
+static struct termios *console_termios[MAX_NR_CONSOLES];
+static struct termios *console_termios_locked[MAX_NR_CONSOLES];
+
+#define NPAR 16
+
+static void con_setsize(unsigned long rows, unsigned long cols);
+static void vc_init(unsigned int console, unsigned long rows, unsigned long cols,
+		    int do_clear);
+static void get_scrmem(int currcons);
+static void set_scrmem(int currcons, long offset);
+static void set_origin(int currcons);
+static void blank_screen(void);
+static void unblank_screen(void);
+void poke_blanked_console(void);
+static void gotoxy(int currcons, int new_x, int new_y);
+static void save_cur(int currcons);
+static inline void set_cursor(int currcons);
+static void reset_terminal(int currcons, int do_clear);
+extern void reset_vc(unsigned int new_console);
+extern void vt_init(void);
+extern void register_console(void (*proc)(const char *));
+extern void compute_shiftstate(void);
+extern void reset_palette (int currcons) ;
+extern void set_palette (void) ;
+
+/* Description of the hardware situation */
+static unsigned long	video_mem_base;		/* Base of video memory		*/
+static unsigned long	video_mem_term;		/* End of video memory		*/
+       unsigned long	video_num_columns;	/* Number of text columns	*/
+static unsigned long	video_num_lines;	/* Number of text lines		*/
+static unsigned long	video_size_row;
+static unsigned long	video_screen_size;
+static unsigned long	video_buf_size;
+static int can_do_color = 1;
+static int printable = 0;			/* Is console ready for printing? */
+
+static unsigned short console_charmask = 0x0ff;
+
+static unsigned char *vc_scrbuf[MAX_NR_CONSOLES];
+
+static int console_blanked = 0;
+static int blankinterval = 10*60*HZ;
+
+struct vc_data {
+	unsigned long	vc_screenbuf_size;
+	unsigned short	vc_video_erase_char;	/* Background erase character */
+	unsigned char	vc_attr;		/* Current attributes */
+	unsigned char	vc_def_forecol;
+	unsigned char	vc_def_backcol;
+	unsigned char	vc_forecol;
+	unsigned char	vc_backcol;
+	unsigned char	vc_s_forecol;
+	unsigned char	vc_s_backcol;
+	unsigned char	vc_cursoron;		/* Cursor on/off */
+	unsigned char	vc_ulcolor;		/* Colour for underline mode */
+	unsigned char	vc_halfcolor;		/* Colour for half intensity mode */
+	unsigned long	vc_origin;		/* Used for EGA/VGA fast scroll	*/
+	unsigned long	vc_scr_end;		/* Used for EGA/VGA fast scroll	*/
+	unsigned long	vc_pos;
+	unsigned long	vc_x,vc_y;
+	unsigned long	vc_top,vc_bottom;
+	unsigned long	vc_cstate;
+	unsigned long	vc_npar,vc_par[NPAR];
+	unsigned long	vc_video_mem_start;	/* Start of video RAM		*/
+	unsigned long	vc_video_mem_end;	/* End of video RAM (sort of)	*/
+	unsigned long	vc_saved_x;
+	unsigned long	vc_saved_y;
+	/* mode flags */
+	unsigned long	vc_charset	: 1;	/* Character set G0 / G1 */
+	unsigned long	vc_s_charset	: 1;	/* Saved character set */
+	unsigned long	vc_disp_ctrl	: 1;	/* Display chars < 32? */
+	unsigned long	vc_toggle_meta	: 1;	/* Toggle high bit? */
+	unsigned long	vc_decscnm	: 1;	/* Screen Mode */
+	unsigned long	vc_decom	: 1;	/* Origin Mode */
+	unsigned long	vc_decawm	: 1;	/* Autowrap Mode */
+	unsigned long	vc_deccm	: 1;	/* Cursor Visible */
+	unsigned long	vc_decim	: 1;	/* Insert Mode */
+	unsigned long	vc_deccolm	: 1;	/* 80/132 Column Mode */
+	/* attribute flags */
+	unsigned long	vc_intensity	: 2;	/* 0=half-bright, 1=normal, 2=bold */
+	unsigned long	vc_underline	: 1;
+	unsigned long	vc_blink	: 1;
+	unsigned long	vc_reverse	: 1;
+	unsigned long	vc_s_intensity	: 2;	/* saved rendition */
+	unsigned long	vc_s_underline	: 1;
+	unsigned long	vc_s_blink	: 1;
+	unsigned long	vc_s_reverse	: 1;
+	/* misc */
+	unsigned long	vc_ques		: 1;
+	unsigned long	vc_need_wrap	: 1;
+	unsigned long	vc_has_scrolled : 1;	/* Info for unblank_screen */
+	unsigned long	vc_kmalloced	: 1;	/* kfree_s() needed */
+	unsigned long	vc_report_mouse : 2;
+	unsigned char	vc_utf		: 1;	/* Unicode UTF-8 encoding */
+	unsigned char	vc_utf_count;
+	unsigned long	vc_utf_char;
+	unsigned long	vc_tab_stop[5];		/* Tab stops. 160 columns. */
+	unsigned short *vc_translate;
+	unsigned char 	vc_G0_charset;
+	unsigned char 	vc_G1_charset;
+	unsigned char 	vc_saved_G0;
+	unsigned char 	vc_saved_G1;
+	unsigned long * vc_paletteentries;
+	/* additional information is in vt_kern.h */
+};
+
+static struct vc {
+	struct vc_data *d;
+
+	/* might add  scrmem, vt_struct, kbd  at some time,
+	   to have everything in one place - the disadvantage
+	   would be that vc_cons etc can no longer be static */
+} vc_cons [MAX_NR_CONSOLES];
+
+#define screenbuf_size	(vc_cons[currcons].d->vc_screenbuf_size)
+#define origin		(vc_cons[currcons].d->vc_origin)
+#define scr_end		(vc_cons[currcons].d->vc_scr_end)
+#define pos		(vc_cons[currcons].d->vc_pos)
+#define top		(vc_cons[currcons].d->vc_top)
+#define bottom		(vc_cons[currcons].d->vc_bottom)
+#define x		(vc_cons[currcons].d->vc_x)
+#define y		(vc_cons[currcons].d->vc_y)
+#define vc_state	(vc_cons[currcons].d->vc_cstate)
+#define npar		(vc_cons[currcons].d->vc_npar)
+#define par		(vc_cons[currcons].d->vc_par)
+#define ques		(vc_cons[currcons].d->vc_ques)
+#define attr		(vc_cons[currcons].d->vc_attr)
+#define saved_x		(vc_cons[currcons].d->vc_saved_x)
+#define saved_y		(vc_cons[currcons].d->vc_saved_y)
+#define translate	(vc_cons[currcons].d->vc_translate)
+#define G0_charset	(vc_cons[currcons].d->vc_G0_charset)
+#define G1_charset	(vc_cons[currcons].d->vc_G1_charset)
+#define saved_G0	(vc_cons[currcons].d->vc_saved_G0)
+#define saved_G1	(vc_cons[currcons].d->vc_saved_G1)
+#define utf		(vc_cons[currcons].d->vc_utf)
+#define utf_count	(vc_cons[currcons].d->vc_utf_count)
+#define utf_char	(vc_cons[currcons].d->vc_utf_char)
+#define video_mem_start	(vc_cons[currcons].d->vc_video_mem_start)
+#define video_mem_end	(vc_cons[currcons].d->vc_video_mem_end)
+#define video_erase_char (vc_cons[currcons].d->vc_video_erase_char)
+#define disp_ctrl	(vc_cons[currcons].d->vc_disp_ctrl)
+#define toggle_meta	(vc_cons[currcons].d->vc_toggle_meta)
+#define decscnm		(vc_cons[currcons].d->vc_decscnm)
+#define decom		(vc_cons[currcons].d->vc_decom)
+#define decawm		(vc_cons[currcons].d->vc_decawm)
+#define deccm		(vc_cons[currcons].d->vc_deccm)
+#define decim		(vc_cons[currcons].d->vc_decim)
+#define deccolm	 	(vc_cons[currcons].d->vc_deccolm)
+#define need_wrap	(vc_cons[currcons].d->vc_need_wrap)
+#define has_scrolled	(vc_cons[currcons].d->vc_has_scrolled)
+#define kmalloced	(vc_cons[currcons].d->vc_kmalloced)
+#define report_mouse	(vc_cons[currcons].d->vc_report_mouse)
+#define forecol		(vc_cons[currcons].d->vc_forecol)
+#define backcol		(vc_cons[currcons].d->vc_backcol)
+#define s_forecol	(vc_cons[currcons].d->vc_s_forecol)
+#define s_backcol	(vc_cons[currcons].d->vc_s_backcol)
+#define def_forecol	(vc_cons[currcons].d->vc_def_forecol)
+#define def_backcol	(vc_cons[currcons].d->vc_def_backcol)
+#define cursoron	(vc_cons[currcons].d->vc_cursoron)
+#define charset		(vc_cons[currcons].d->vc_charset)
+#define s_charset	(vc_cons[currcons].d->vc_s_charset)
+#define	intensity	(vc_cons[currcons].d->vc_intensity)
+#define	underline	(vc_cons[currcons].d->vc_underline)
+#define	blink		(vc_cons[currcons].d->vc_blink)
+#define	reverse		(vc_cons[currcons].d->vc_reverse)
+#define	s_intensity	(vc_cons[currcons].d->vc_s_intensity)
+#define	s_underline	(vc_cons[currcons].d->vc_s_underline)
+#define	s_blink		(vc_cons[currcons].d->vc_s_blink)
+#define	s_reverse	(vc_cons[currcons].d->vc_s_reverse)
+#define tab_stop	(vc_cons[currcons].d->vc_tab_stop)
+#define paletteentries	(vc_cons[currcons].d->vc_paletteentries)
+
+#define vcmode		(vt_cons[currcons]->vc_mode)
+#define structsize	(sizeof(struct vc_data) + sizeof(struct vt_struct))
+
+/* ARC Extention */
+char cmap_80[][8]=
+{
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /*   */
+ {0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00}, /* ! */
+ {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, /* " */
+ {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00}, /* # */
+ {0x0C,0x3F,0x68,0x3E,0x0B,0x7E,0x18,0x00}, /* $ */
+ {0x60,0x66,0x0C,0x18,0x30,0x66,0x06,0x00}, /* % */
+ {0x38,0x6C,0x6C,0x38,0x6D,0x66,0x3B,0x00}, /* & */
+ {0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, /* ' */
+ {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, /* ( */
+ {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, /* ) */
+ {0x00,0x18,0x7E,0x3C,0x7E,0x18,0x00,0x00}, /* * */
+ {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00}, /* + */
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, /* , */
+ {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, /* - */
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, /* . */
+ {0x00,0x06,0x0C,0x18,0x30,0x60,0x00,0x00}, /* / */
+ {0x3C,0x66,0x6E,0x7E,0x76,0x66,0x3C,0x00}, /* 0 */
+ {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
+ {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
+ {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
+ {0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
+ {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
+ {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
+ {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
+ {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
+ {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
+ {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x00}, /* : */
+ {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x30}, /* ; */
+ {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, /* < */
+ {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00}, /* = */
+ {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, /* > */
+ {0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00}, /* ? */
+ {0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3C,0x00}, /* @ */
+ {0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
+ {0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
+ {0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
+ {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
+ {0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
+ {0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
+ {0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00}, /* G */
+ {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* H */
+ {0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00}, /* I */
+ {0x3E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}, /* J */
+ {0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00}, /* K */
+ {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}, /* L */
+ {0x63,0x77,0x7F,0x6B,0x6B,0x63,0x63,0x00}, /* M */
+ {0x66,0x66,0x76,0x7E,0x6E,0x66,0x66,0x00}, /* N */
+ {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* O */
+ {0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00}, /* P */
+ {0x3C,0x66,0x66,0x66,0x6A,0x6C,0x36,0x00}, /* Q */
+ {0x7C,0x66,0x66,0x7C,0x6C,0x66,0x66,0x00}, /* R */
+ {0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00}, /* S */
+ {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* T */
+ {0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* U */
+ {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, /* V */
+ {0x63,0x63,0x6B,0x6B,0x7F,0x77,0x63,0x00}, /* W */
+ {0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00}, /* X */
+ {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00}, /* Y */
+ {0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00}, /* Z */
+ {0x7C,0x60,0x60,0x60,0x60,0x60,0x7C,0x00}, /* [ */
+ {0x00,0x60,0x30,0x18,0x0C,0x06,0x00,0x00}, /* \ */
+ {0x3E,0x06,0x06,0x06,0x06,0x06,0x3E,0x00}, /* ] */
+ {0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00}, /* ^ */
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, /* _ */
+ {0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00}, /* ` */
+ {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00}, /* a */
+ {0x60,0x60,0x7C,0x66,0x66,0x66,0x7C,0x00}, /* b */
+ {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, /* c */
+ {0x06,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00}, /* d */
+ {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, /* e */
+ {0x1C,0x30,0x30,0x7C,0x30,0x30,0x30,0x00}, /* f */
+ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x3C}, /* g */
+ {0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x00}, /* h */
+ {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, /* i */
+ {0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x70}, /* j */
+ {0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00}, /* k */
+ {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, /* l */
+ {0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x00}, /* m */
+ {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, /* n */
+ {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, /* o */
+ {0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60}, /* p */
+ {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x07}, /* q */
+ {0x00,0x00,0x6C,0x76,0x60,0x60,0x60,0x00}, /* r */
+ {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, /* s */
+ {0x30,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00}, /* t */
+ {0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00}, /* u */
+ {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, /* v */
+ {0x00,0x00,0x63,0x6B,0x6B,0x7F,0x36,0x00}, /* w */
+ {0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00}, /* x */
+ {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x3C}, /* y */
+ {0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00}, /* z */
+ {0x0C,0x18,0x18,0x70,0x18,0x18,0x0C,0x00}, /* { */
+ {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* | */
+ {0x30,0x18,0x18,0x0E,0x18,0x18,0x30,0x00}, /* } */
+ {0x31,0x6B,0x46,0x00,0x00,0x00,0x00,0x00}, /* ~ */
+ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}  /*  */
+};
+
+static unsigned long palette_4[]=
+{
+	0x1000,       /* Black */
+	0x100c,       /* Red */
+	0x10c0,       /* Green */
+	0x10cc,       /* Yellow */
+	0x1c00,       /* Blue */
+	0x1c0c,       /* Magenta */
+	0x1cc0,       /* Cyan */
+	0x1ccc,       /* White */
+	0x1000,
+	0x100f,
+	0x10f0,
+	0x10ff,
+	0x1f00,
+	0x1f0F,
+	0x1ff0,
+	0x1fff,
+	0x1000
+};
+
+static unsigned long palette_8[]=
+{
+	0x1000,
+	0x1111,
+	0x1222,
+	0x1333,
+	0x1400,
+	0x1511,
+	0x1622,
+	0x1733,
+	0x1004,
+	0x1115,
+	0x1226,
+	0x1337,
+	0x1404,
+	0x1515,
+	0x1626,
+	0x1737,
+	0x1000
+};
+
+static unsigned long *default_palette_entries = palette_4;
+
+/* -------------------------------------------------------------------------------
+ * Low level char write
+ * ------------------------------------------------------------------------------- */
+
+static inline void charwrite(int currcons,unsigned long ps,int ch)
+{
+  unsigned long *buffer = (unsigned long *)vc_scrbuf[currcons];
+  unsigned int index;
+  unsigned char flgs;
+
+  flgs=FLAGS;
+
+  if(currcons == fg_console)
+    ll_char_write(ps, ch, forecol, backcol, flgs);
+
+  index = (x + y * video_num_columns);
+
+  buffer[index] = (flgs<<24)|(backcol<<16)|(forecol<<8)|ch;
+}
+
+static char cursor_on=0;
+unsigned long cp;
+
+static inline void remove_cursors(int currcons)
+{
+  unsigned long flags;
+
+  save_flags(flags);
+  cli();
+  if(--cursoron==0 && currcons == fg_console)
+    put_cursor(0, cp);
+  restore_flags(flags);
+}
+
+static inline void restore_cursors(int currcons)
+{
+  unsigned long flags;
+
+  save_flags(flags);
+  cli();
+  if(++cursoron==1 && cursor_on && currcons == fg_console)
+    put_cursor(1,cp);
+  restore_flags(flags);
+}
+
+/* -----------------------------------------------------------------------------------------
+ * VC stuff
+ * ----------------------------------------------------------------------------------------- */
+
+int vc_cons_allocated(unsigned int i)
+{
+  	return (i < MAX_NR_CONSOLES && vc_cons[i].d);
+}
+
+int vc_allocate(unsigned int i)
+{
+	if (i >= MAX_NR_CONSOLES)
+	  return -ENODEV;
+	if (!vc_cons[i].d) {
+	    long p, q;
+
+	    /* prevent users from taking too much memory */
+	    if (i >= MAX_NR_USER_CONSOLES && !suser())
+	      return -EPERM;
+
+	    /* due to the granularity of kmalloc, we waste some memory here */
+	    /* the alloc is done in two steps, to optimize the common situation
+	       of a 25x80 console (structsize=216, video_screen_size=4000) */
+	    q = (long) kmalloc(video_buf_size, GFP_KERNEL);
+	    if (!q)
+	      return -ENOMEM;
+	    p = (long) kmalloc(structsize, GFP_KERNEL);
+	    if (!p) {
+		kfree_s((char *) q, video_buf_size);
+		return -ENOMEM;
+	    }
+
+	    memset((void *)q, 0, video_buf_size);
+
+	    vc_cons[i].d = (struct vc_data *) p;
+	    p += sizeof(struct vc_data);
+	    vt_cons[i] = (struct vt_struct *) p;
+	    vc_scrbuf[i] = (unsigned char *) q;
+	    vc_cons[i].d->vc_kmalloced = 1;
+	    vc_cons[i].d->vc_screenbuf_size = video_buf_size;
+	    vc_init (i, video_num_lines, video_num_columns, 1);
+	}
+	return 0;
+}
+
+int vc_resize(unsigned long lines, unsigned long cols)
+{/* TODO */
+	return -ENOMEM;
+}
+
+void vc_disallocate(unsigned int currcons)
+{
+	if(vc_cons_allocated(currcons))
+	{
+	  if(kmalloced)
+	    kfree_s(vc_scrbuf[currcons], screenbuf_size);
+	  if(currcons >= MIN_NR_CONSOLES)
+	    kfree_s(vc_cons[currcons].d, structsize);
+	  vc_cons[currcons].d = 0;
+	}
+}
+
+#define set_kbd(x) set_vc_kbd_mode(kbd_table+currcons,x)
+#define clr_kbd(x) clr_vc_kbd_mode(kbd_table+currcons,x)
+#define is_kbd(x) vc_kbd_mode(kbd_table+currcons,x)
+
+#define decarm		VC_REPEAT
+#define decckm		VC_CKMODE
+#define kbdapplic	VC_APPLIC
+#define lnm		VC_CRLF
+
+/*
+ * this is what the terminal answers to a ESC-Z or csi0c query.
+ */
+#define VT100ID "\033[?1;2c"
+#define VT102ID "\033[?6c"
+
+static unsigned char color_4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
+static unsigned char color_8[] = {0x00, 0x18, 0x60, 0x78, 0x84, 0x9C, 0xE4, 0xFC,
+                                  0x00, 0x1B, 0x63, 0x7B, 0x87, 0x9F, 0xE7, 0xFF};
+static unsigned char *color_table = color_4;
+
+/*
+ * gotoxy() must verify all boundaries, because the arguments
+ * might also be negative. If a given position is out of
+ * bounds, the cursor is placed at the nearest margin.
+ */
+static void gotoxy(int currcons,int new_x,int new_y)
+{
+  int max_y;
+
+  if(new_x<0)
+    x = 0;
+  else
+  if(new_x>=video_num_columns)
+    x = video_num_columns-1;
+  else
+    x = new_x;
+
+  if(decom)
+  {
+    new_y += top;
+    max_y = bottom;
+  }
+  else
+    max_y=video_num_lines;
+
+  if(new_y < 0)
+    y = 0;
+  else
+  if(new_y >= max_y)
+    y = max_y-1;
+  else
+    y = new_y;
+
+  pos = origin + (y*video_num_columns*bytes_per_char_v+x)*bytes_per_char_h;
+  need_wrap=0;
+}
+
+static unsigned long __real_origin;
+static unsigned long __origin;		/* Offset of currently displayed screen */
+
+static void __set_origin(unsigned long offset)
+{
+  unsigned long flags;
+  clear_selection();
+  save_flags(flags); cli();
+  __origin = offset;
+
+  memc_write(0,offset>>2);
+  memc_write(1,0);
+  memc_write(2,video_addr_mask>>2);
+  restore_flags(flags);
+}
+
+/*
+void scrollback(int lines)
+{
+}
+
+void scrollfront(int lines)
+{
+}
+ */
+
+static void set_origin(int currcons)
+{
+	if(currcons != fg_console || vcmode == KD_GRAPHICS)
+		return;
+	__real_origin = origin - video_mem_start;
+	if(__real_origin >= video_addr_mask)
+		__real_origin -= video_addr_mask;
+	__set_origin(__real_origin);
+}
+
+static void hide_cursor(void)
+{
+	put_cursor(0,-1);
+}
+
+static void set_cursor(int currcons)
+{
+	unsigned long flags;
+
+	if(currcons != fg_console || vcmode == KD_GRAPHICS)
+		return;
+
+	if(__real_origin != __origin)
+		__set_origin(__real_origin);
+	save_flags(flags);
+	cli();
+
+	if(deccm)
+		cp = pos + video_num_columns * bytes_per_char_h * (bytes_per_char_v - 1);
+	else
+		hide_cursor();
+	restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------------------
+ * Screen scrolling
+ * -------------------------------------------------------------------------------- */
+
+static void scrup(int currcons,unsigned int t, unsigned int b,unsigned char l)
+{
+	unsigned char flgs;
+
+	if(b > video_num_columns || t >= b)
+		return;
+
+	if(currcons == fg_console)
+	{
+		if(t || b != video_num_lines)
+		{
+			if(l<t-b)
+				memmove((void*)(origin+t*video_size_row),
+					    (void*)(origin+(t+l)*video_size_row),
+					    (b-t-l)*video_size_row);
+			else
+			if(l>b-t)
+				l=b-t;
+
+			memfastset((void*)(origin+(b-l)*video_size_row),
+					0x11111111*backcol,l*video_size_row);
+		}
+		else
+		{
+			memfastset((void*)(origin+l*video_size_row*video_num_lines),
+				0x11111111L*backcol,l*video_size_row);
+			pos-=origin;
+			origin+=l*video_size_row;
+			if(origin>=video_mem_term)
+				origin=origin-video_mem_term+video_mem_base;
+			pos+=origin;
+			set_origin(currcons);
+		}
+	}
+	else
+	{
+		if(!t && b == video_num_lines)
+		{
+			pos-=origin;
+			origin+=l*video_size_row;
+			if(origin>=video_mem_term)
+				origin=origin-video_mem_term+video_mem_base;
+			pos+=origin;
+		}
+        }
+	if(l < t-b)
+		memmove((void*)(vc_scrbuf[currcons]+t*video_num_columns*4),
+			(void*)(vc_scrbuf[currcons]+(t+l)*video_num_columns*4),
+			(b-t-l)*video_num_columns*4);
+	else
+	if(l > b-t)
+		l=b-t;
+
+	flgs=FLAGS;
+
+	memfastset((void*)(vc_scrbuf[currcons]+(b-l)*video_num_columns*4),
+		(flgs << 24)|(backcol << 16)|(forecol << 8)|32,
+		l*video_num_columns*4);
+}
+
+static void scrdown(int currcons,unsigned int t,unsigned int b,unsigned char l)
+{
+	unsigned char flgs;
+
+	if(b>video_num_columns || t >= b)
+		return;
+
+	if(currcons == fg_console)
+	{
+		if(t || b != video_num_lines)
+		{
+			if(l<t-b)
+				memmove((void*)(origin+(t+l)*video_size_row),
+					    (void*)(origin+t*video_size_row),
+						(b-t-l)*video_size_row);
+			else
+			if(l>b-t)
+				l=b-t;
+
+			memfastset((void*)(origin+t*video_size_row),
+					0x11111111*backcol,l*video_size_row);
+		}
+		else
+		{
+			pos-=origin;
+			origin-=l*video_size_row;
+			if(origin<video_mem_base)
+				origin=origin-video_mem_base+video_mem_term;
+			pos+=origin;
+			memfastset((void*)origin,0x11111111L*backcol,l*video_size_row);
+			set_origin(currcons);
+		}
+	}
+	else
+	{
+		if(!t && b == video_num_lines)
+		{
+			pos-=origin;
+			origin-=l*video_size_row;
+			if(origin<video_mem_base)
+				origin=origin-video_mem_base+video_mem_term;
+			pos+=origin;
+		}
+	}
+	if(l < t-b)
+		memmove((void*)(vc_scrbuf[currcons]+(t+l)*video_num_columns*4),
+			(void*)(vc_scrbuf[currcons]+t*video_num_columns*4),
+			(b-t-l)*video_num_columns*4);
+	else
+	if(l > b-t)
+		l = b-t;
+
+	flgs=FLAGS;
+	memfastset((void*)(vc_scrbuf[currcons]+t*video_num_columns*4),
+		(flgs << 24)|(backcol << 16)|(forecol << 8)|32,
+		l*video_num_columns*4);
+}
+
+static void lf(int currcons)
+{
+	if (y + 1 < bottom) {
+		y++;
+		pos += video_size_row;
+		return;
+	}
+	scrup(currcons,top,bottom,1);
+	need_wrap=0;
+}
+
+static void ri(int currcons)
+{
+	if (y > top) {
+		y--;
+		pos -= video_size_row;
+		return;
+	}
+	scrdown(currcons,top,bottom,1);
+	need_wrap=0;
+}
+
+static inline void cr(int currcons)
+{
+	pos -= x*bytes_per_char_h;
+	need_wrap = x = 0;
+}
+
+static inline void bs(int currcons)
+{
+	if (x) {
+		if (!need_wrap) {
+			pos -= bytes_per_char_h;
+			x--;
+		}
+		need_wrap = 0;
+	}
+}
+
+static inline void del(int currcons)
+{
+  /* ignored */
+}
+
+static void csi_J(int currcons, int vpar)
+{
+	unsigned char countx,county;
+	unsigned char startx,starty;
+
+	switch(vpar) {
+		case 0: /* erase from cursor to bottom of screen */
+			startx=x;
+			starty=y;
+			countx=video_num_columns-x;
+			county=video_num_lines-y-1;
+			break;
+		case 1: /* erase from top of screen to cursor */
+			startx=0;
+			starty=0;
+			countx=x;
+			county=y;
+			break;
+		case 2: /* erase entire screen */
+			startx=0;
+			starty=0;
+			countx=video_num_columns;
+			county=video_num_lines-1;
+			origin=video_mem_base;
+			set_origin(currcons);
+			gotoxy(currcons,x,y);
+			break;
+		default:
+			return;
+	}
+	ll_erase(currcons,startx,starty,countx,county);
+	need_wrap = 0;
+}
+
+static void csi_K(int currcons, int vpar)
+{
+	unsigned char countx;
+	unsigned char startx;
+
+	switch(vpar) {
+		case 0: /* erase from cursor to end of line */
+			startx = x;
+			countx = video_num_columns - x;
+			break;
+		case 1: /* erase from beginning of line to cursor */
+			startx = 0;
+			countx = x;
+			break;
+		case 2: /* erase entire line */
+			startx=0;
+			countx=video_num_columns;
+			break;
+		default:
+			return;
+	}
+	ll_erase(currcons,startx,y,countx,0);
+	need_wrap = 0;
+}
+
+static void csi_X(int currcons, int vpar) /* erase the following vpar positions */
+{					  /* not vt100? */
+	unsigned char countx,county;
+	unsigned char startx,starty;
+
+	if (!vpar)
+		vpar++;
+
+	startx = x;
+	starty = y;
+	countx = 0;
+	county = 0;
+#if 0 /* TODO */
+	ll_erase(currcons,startx,starty,countx,county);
+#endif
+	need_wrap = 0;
+}
+
+
+/*
+static void update_addr(int currcons)
+{
+
+}
+ */
+static void default_attr(int currcons)
+{
+	underline = 0;
+	reverse = 0;
+	blink = 0;
+	intensity = 0;
+
+	forecol = def_forecol;
+	backcol = def_backcol;
+}
+
+static void csi_m(int currcons)
+{
+  int i;
+
+  for(i=0;i<=npar;i++)
+  {
+    switch(par[i])
+    {
+      case 0:	default_attr(currcons); break;
+      case 1:	intensity|=BOLD;	break;	/* Bold */
+      case 2:	intensity&=~BOLD;	break;	/* Feint */
+      case 3:	intensity|=ITALIC;	break;	/* Italic */
+      case 4:	underline = 1;		break;	/* Underline */
+      case 5:
+      case 6:	blink = 1;		break;	/* Flash */
+      case 7:	reverse = 1;		break;	/* Inverse chars */
+
+			case 10: /* ANSI X3.64-1979 (SCO-ish?)
+				  * Select primary font, don't display
+				  * control chars if defined, don't set
+				  * bit 8 on output.
+				  */
+				translate = set_translate(charset == 0
+						? G0_charset
+						: G1_charset);
+				disp_ctrl = 0;
+				toggle_meta = 0;
+				break;
+			case 11: /* ANSI X3.64-1979 (SCO-ish?)
+				  * Select first alternate font, let's
+				  * chars < 32 be displayed as ROM chars.
+				  */
+				translate = set_translate(IBMPC_MAP);
+				disp_ctrl = 1;
+				toggle_meta = 0;
+				break;
+			case 12: /* ANSI X3.64-1979 (SCO-ish?)
+				  * Select second alternate font, toggle
+				  * high bit before displaying as ROM char.
+				  */
+				translate = set_translate(IBMPC_MAP);
+				disp_ctrl = 1;
+				toggle_meta = 1;
+				break;
+
+      case 21:
+      case 22:  intensity = 0;	break;
+      case 24:  underline = 0;	break;
+      case 25:  blink = 0;	break;
+      case 27:	reverse = 0;	break;
+      case 30:
+      case 31:
+      case 32:
+      case 33:
+      case 34:
+      case 35:
+      case 36:
+      case 37:  forecol=color_table[par[i]-30];		break;	/* Foreground colour */
+      case 38:  forecol=def_forecol; underline = 1;	break;
+      case 39:  forecol=def_forecol; underline = 0;	break;	/* Default foreground colour */
+      case 40:
+      case 41:
+      case 42:
+      case 43:
+      case 44:
+      case 45:
+      case 46:
+      case 47:  backcol=color_table[par[i]-40];	break;	/* Background colour */
+      case 49:  backcol=def_backcol;		break;	/* Default background colour */
+    }
+  }
+}
+
+static void respond_string(char *p,struct tty_struct *tty)
+{
+	while(*p)
+		tty_insert_flip_char(tty, *p++, 0);
+	tty_schedule_flip(tty);
+}
+
+static void cursor_report(int currcons,struct tty_struct *tty)
+{
+	char buf[40];
+
+	sprintf(buf, "\033[%ld;%ldR",y + (decom ? top+1 : 1),
+				     x + 1);
+	respond_string(buf, tty);
+}
+
+static void status_report(struct tty_struct *tty)
+{
+	respond_string("\033[0n",tty);
+}
+
+static void respond_ID(struct tty_struct *tty)
+{
+	respond_string(VT102ID,tty);
+}
+
+static void mouse_report(int currcons, struct tty_struct *tty, int butt, int mrx, int mry)
+{
+	char buf[8];
+
+	sprintf(buf,"\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
+		(char)('!'+mry));
+	respond_string(buf,tty);
+}
+
+/* invoked by ioctl(TIOCLINUX) */
+int mouse_reporting(void)
+{
+	int currcons = fg_console;
+
+	return report_mouse;
+}
+
+static void invert_screen(void)
+{
+  /* Todo */
+}
+
+unsigned long *screen_pos(int currcons, int screen_offset)
+{
+	return (unsigned long *)(vc_scrbuf[currcons] + screen_offset * 4);
+}
+
+void getconsxy(int currcons, char *p)
+{
+	p[0] = x;
+	p[1] = y;
+}
+
+void putconsxy(int currcons, char *p)
+{
+	gotoxy(currcons, p[0], p[1]);
+	set_cursor(currcons);
+}
+
+static void set_mode(int currcons, int on_off)
+{
+	int i;
+
+	for(i=0;i<=npar;i++)
+		if(ques) switch(par[i]) {	/* DEC private modes set/reset */
+			case 1:			/* Cursor keys send ^[Ox/^[[x */
+				if (on_off)
+					set_kbd(decckm);
+				else
+					clr_kbd(decckm);
+				break;
+			case 3: 		/* 80/132 mode switch unimplemented */
+				deccolm = on_off;
+#if 0
+				csi_J(currcons,2);
+				gotoxy(currcons,0,0);
+#endif
+				break;
+			case 5:			/* Inverted screen on/off */
+				if (decscnm != on_off) {
+					decscnm = on_off;
+					invert_screen();
+/* 					update_attr(); */
+				}
+				break;
+			case 6:			/* Origin relative/absolute */
+				decom = on_off;
+				gotoxy(currcons,0,0);
+				break;
+			case 7:			/* Autowrap on/off */
+				decawm = on_off;
+				break;
+			case 8:			/* Autorepeat on/off */
+				if (on_off)
+					set_kbd(decarm);
+				else
+					clr_kbd(decarm);
+				break;
+			case 9:
+				report_mouse = on_off ? 1 : 0;
+				break;
+			case 25:		/* Cursor on/off */
+				deccm = on_off;
+				set_cursor(currcons);
+				break;
+			case 1000:
+				report_mouse = on_off ? 2 : 0;
+				break;
+		} else switch(par[i]) {		/* ANSI modes set/reset */
+			case 3:			/* Monitor (display ctrls) */
+				disp_ctrl = on_off;
+				break;
+			case 4:			/* Insert mode on/off */
+				decim = on_off;
+				break;
+			case 20:		/* Lf, Enter = CrLf/Lf */
+				if (on_off)
+					set_kbd(lnm);
+				else
+					clr_kbd(lnm);
+				break;
+		}
+}
+
+static void setterm_command(int currcons)
+{
+	switch(par[0]) {
+		case 1: /* Set colour for underline mode (implemented as an underline) */
+			break;
+		case 2: /* set colour for half intensity mode (implemented as half) */
+			break;
+		case 8:
+			def_forecol = forecol;
+			def_backcol = backcol;
+			break;
+		case 9:
+			blankinterval=((par[1]<60)?par[1]:60)*60*HZ;
+			break;
+	}
+}
+
+static void insert_char(int currcons)
+{
+	register unsigned char *c,*cc;
+	register unsigned char row;
+	register int col;
+
+	c = (unsigned char*)pos;
+	cc = (unsigned char*)pos + bytes_per_char_h;
+
+	for (row = 0; row < bytes_per_char_v; row++) {
+		for(col = (video_num_columns - x - 1) * bytes_per_char_h - 1; col >= 0; col--)
+			cc[col] = c[col];
+		*c = 0x11*backcol;
+		c += video_num_columns * bytes_per_char_h;
+		cc += video_num_columns * bytes_per_char_h;
+	}
+}
+
+static void insert_line(int currcons,int n)
+{
+	scrdown(currcons,y,bottom,n);
+	need_wrap = 0;
+}
+
+static void delete_char(int currcons)
+{
+	register unsigned char *c, *cc;
+	register unsigned char row;
+	register unsigned int  col;
+
+	c = (unsigned char*)pos;
+	cc = (unsigned char*)pos + bytes_per_char_h;
+
+	for (row=0; row < bytes_per_char_v; row++) {
+		for (col=0; col < bytes_per_char_h * (video_num_columns - x - 1); col++) {
+			c[col] = cc[col];
+			cc[col] = 0x11*backcol;
+		}
+		c += video_num_columns * bytes_per_char_h;
+		cc += video_num_columns * bytes_per_char_h;
+	}
+}
+
+static void delete_line(int currcons,int n)
+{
+	scrup(currcons,y,bottom,n);
+	need_wrap = 0;
+}
+
+static void csi_at(int currcons,int nr)
+{
+	if (nr > video_num_columns - x)
+		nr = video_num_columns-x;
+	else if (!nr)
+		nr = 1;
+	while(nr--)
+		insert_char(currcons);
+}
+
+static void csi_L(int currcons,int nr)
+{
+	if (nr > video_num_lines - y)
+		nr = video_num_lines - y;
+	else if (!nr)
+		nr = 1;
+	insert_line(currcons,nr);
+}
+
+static void csi_P(int currcons,int nr)
+{
+	if (nr > video_num_columns)
+		nr = video_num_columns;
+	else if (!nr)
+		nr = 1;
+	while(nr--)
+		delete_char(currcons);
+}
+
+static void csi_M(int currcons,int nr)
+{
+	if (nr > video_num_lines)
+		nr = video_num_lines;
+	else if (!nr)
+		nr = 1;
+	delete_line(currcons,nr);
+}
+
+static void save_cur(int currcons)
+{
+	saved_x		= x;
+	saved_y		= y;
+	saved_G0	= G0_charset;
+	saved_G1	= G1_charset;
+	s_intensity	= intensity;
+	s_blink		= blink;
+	s_underline	= underline;
+	s_reverse	= reverse;
+	s_forecol	= forecol;
+	s_backcol	= backcol;
+}
+
+static void restore_cur(int currcons)
+{
+	gotoxy(currcons,saved_x,saved_y);
+	intensity	= s_intensity;
+	underline	= s_underline;
+	blink		= s_blink;
+	reverse		= s_reverse;
+	forecol		= s_forecol;
+	backcol		= s_backcol;
+	G0_charset	= saved_G0;
+	G1_charset	= saved_G1;
+	need_wrap	= 0;
+}
+
+enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
+	EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
+	ESpalette };
+
+static void reset_terminal(int currcons,int do_clear)
+{
+	top		= 0;
+	bottom		= video_num_lines;
+	vc_state	= ESnormal;
+	ques		= 0;
+	translate	= set_translate(LAT1_MAP);
+	G0_charset	= LAT1_MAP;
+	G1_charset	= GRAF_MAP;
+	charset		= 0;
+	need_wrap	= 0;
+	report_mouse	= 0;
+	utf		= 0; /* ? *** */
+	utf_count	= 0;
+
+	paletteentries	= 0;
+	disp_ctrl	= 0;
+	toggle_meta	= 0;
+
+	def_forecol	= color_table[7];
+	def_backcol	= color_table[0];
+	forecol	= color_table[7];
+	backcol	= color_table[0];
+
+	decscnm		= 0;
+	decom		= 0;
+	decawm		= 1;
+	deccm		= 1;
+	decim		= 0;
+
+	set_kbd(decarm);
+	clr_kbd(decckm);
+	clr_kbd(kbdapplic);
+	clr_kbd(lnm);
+	kbd_table[currcons].lockstate = 0;
+	kbd_table[currcons].ledmode = LED_SHOW_FLAGS;
+	kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate;
+	set_leds();
+
+	default_attr(currcons);
+
+	tab_stop[0]	= 0x01010100;
+	tab_stop[1]	=
+	tab_stop[2]	=
+	tab_stop[3]	=
+	tab_stop[4]	= 0x01010101;
+
+	gotoxy(currcons,0,0);
+	save_cur(currcons);
+	if (do_clear)
+	    csi_J(currcons,2);
+}
+
+/*
+ * Turn the Scroll-Lock LED on when the tty is stopped
+ */
+static void con_stop(struct tty_struct *tty)
+{
+	int console_num;
+	if (!tty)
+		return;
+	console_num = MINOR(tty->device) - (tty->driver.minor_start);
+	if (!vc_cons_allocated(console_num))
+		return;
+	set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
+	set_leds();
+}
+
+/*
+ * Turn the Scroll-Lock LED off when the console is started
+ */
+static void con_start(struct tty_struct *tty)
+{
+	int console_num;
+	if(!tty)
+		return;
+	console_num = MINOR(tty->device) - (tty->driver.minor_start);
+	if (!vc_cons_allocated(console_num))
+		return;
+	clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
+	set_leds();
+}
+
+static int con_write(struct tty_struct * tty, int from_user,
+		     const unsigned char *buf, int count)
+{
+	int c, tc, ok, n = 0, r = 0;
+	unsigned int currcons;
+	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+	unsigned char buffer[32], *p = buffer;
+
+	currcons = vt->vc_num;
+	if (!vc_cons_allocated(currcons)) {
+	    /* could this happen? */
+	    static int error = 0;
+	    if(!error) {
+		error = 1;
+		printk("con_write: tty %d not allocated\n", currcons+1);
+	    }
+	    return 0;
+	}
+
+	remove_cursors(currcons);
+	if(currcons == sel_cons)
+		clear_selection();
+
+	disable_bh(KEYBOARD_BH);
+	while(!tty->stopped && count) {
+		if(from_user && r == 0)
+		{
+			r = count;
+			if(r > 32) r = 32;
+			memcpy_fromfs(buffer, buf, r);
+			p = buffer;
+    		}
+		c = from_user ? *p++ : *buf;
+		buf++; n++; count--;
+		if(from_user) r--;
+
+		if (utf) {
+		    /* Combine UTF-8 into Unicode */
+		    /* Incomplete characters silently ignored */
+		    if(c > 0x7f) {
+			if (utf_count > 0 && (c & 0xc0) == 0x80) {
+				utf_char = (utf_char << 6) | (c & 0x3f);
+				utf_count--;
+				if (utf_count == 0)
+				    tc = c = utf_char;
+				else continue;
+			} else {
+				if ((c & 0xe0) == 0xc0) {
+				    utf_count = 1;
+				    utf_char = (c & 0x1f);
+				} else if ((c & 0xf0) == 0xe0) {
+				    utf_count = 2;
+				    utf_char = (c & 0x0f);
+				} else if ((c & 0xf8) == 0xf0) {
+				    utf_count = 3;
+				    utf_char = (c & 0x07);
+				} else if ((c & 0xfc) == 0xf8) {
+				    utf_count = 4;
+				    utf_char = (c & 0x03);
+				} else if ((c & 0xfe) == 0xfc) {
+				    utf_count = 5;
+				    utf_char = (c & 0x01);
+				} else
+				    utf_count = 0;
+				continue;
+			}
+		    } else {
+		      tc = c;
+		      utf_count = 0;
+		    }
+		} else {	/* no utf */
+		  tc = translate[toggle_meta ? (c | 0x80) : c];
+		}
+		
+		/* If the origional code was < 32 we only allow a
+		 * glyph to be displayed if the code is not normally
+		 * used (such as for cursor movement) or if the
+		 * disp_ctrl mode has been explicitly enabled.
+		 * Note: ESC is *never* allowed to be displayed as
+		 * that would disable all escape sequences!
+		 * To display font position 0x1B, go into UTF mode
+		 * and display character U+F01B, or change the mapping.
+		 */
+		ok = (tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS
+						: CTRL_ACTION) >> c) & 1))));
+
+		if (vc_state == ESnormal && ok) {
+		    /* Now try to find out how to display it */
+			tc = conv_uni_to_pc(tc);
+			if (tc == -4) {
+			  /* If we got -4 (not found) then see if we have
+			     defined a replacement character (U+FFFD) */
+				tc = conv_uni_to_pc(0xfffd);
+			} else if (tc == -3) {
+				/* Bad hash table -- hope for the best */
+				tc = c;
+			}
+			if (tc & ~console_charmask)
+				continue; /* Conversion failed */
+
+			if (need_wrap) {
+				cr(currcons);
+				lf(currcons);
+      			}
+			if(decim)
+				insert_char(currcons);
+			charwrite(currcons, pos, tc);
+			if (x == video_num_columns - 1)
+				need_wrap = decawm;
+			else {
+				x++;
+				pos+=bytes_per_char_h;
+			}
+			continue;
+		}
+
+		/*
+		 *  Control characters can be used in the _middle_
+		 *  of an escape sequence.
+		 */
+		switch(c) {
+			case 7:
+				kd_mksound(0x637, HZ/8);
+				continue;
+			case 8:
+				bs(currcons);
+				continue;
+			case 9:
+				pos -= x * bytes_per_char_h;
+				while (x < video_num_columns - 1) {
+					x++;
+					if (tab_stop[x >> 5] & (1 << (x & 31)))
+						break;
+				}
+				pos += x * bytes_per_char_h;
+				continue;
+			case 10: case 11: case 12:
+				lf(currcons);
+				if(!is_kbd(lnm))
+					continue;
+			case 13:
+				cr(currcons);
+				continue;
+			case 14:
+				charset = 1;
+				translate = set_translate(G1_charset);
+				disp_ctrl = 1;
+				continue;
+			case 15:
+				charset = 0;
+				translate = set_translate(G0_charset);
+				disp_ctrl = 0;
+				continue;
+			case 24: case 26:
+				vc_state = ESnormal;
+				continue;
+			case 27:
+				vc_state = ESesc;
+				continue;
+			case 127:
+				del(currcons);
+				continue;
+			case 128+27:
+				vc_state = ESsquare;
+				continue;
+    		}
+		switch(vc_state) {
+			case ESesc:
+				vc_state = ESnormal;
+				switch (c) {
+					case '[':
+						vc_state = ESsquare;
+						continue;
+					case '%':
+						vc_state = ESpercent;
+						continue;
+					case 'E':
+						cr(currcons);
+						lf(currcons);
+						continue;
+					case 'M':
+						ri(currcons);
+						continue;
+					case 'D':
+						lf(currcons);
+						continue;
+					case 'H':
+						tab_stop[x >> 5] |= (1 << (x & 31));
+						continue;
+					case 'Z':
+						respond_ID(tty);
+						continue;
+					case '7':
+						save_cur(currcons);
+						continue;
+					case '8':
+						restore_cur(currcons);
+						continue;
+					case '(':
+						vc_state = ESsetG0;
+						continue;
+					case ')':
+						vc_state = ESsetG1;
+						continue;
+					case '#':
+						vc_state = EShash;
+						continue;
+					case 'c':
+						reset_terminal(currcons,1);
+						continue;
+					case '>': /* Numeric keypad */
+						clr_kbd(kbdapplic);
+						continue;
+					case '=': /* Appl. keypad */
+						set_kbd(kbdapplic);
+						continue;
+				}
+				continue;
+			case ESnonstd:
+				if (c=='P') {   /* palette escape sequence */
+					for (npar = 0; npar<NPAR; npar++)
+						par[npar] = 0;
+					npar = 0 ;
+					vc_state = ESpalette;
+					continue;
+				} else if (c=='R') { /* reset palette */
+					reset_palette (currcons);
+					vc_state = ESnormal;
+				} else
+					vc_state = ESnormal;
+				continue;
+			case ESpalette:
+				if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
+					par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
+					if(npar==7) {
+#if 0
+						int i = par[0]*3, j = 1;
+						palette[i] = 16*par[j++];
+						palette[i++] += par[j++];
+						palette[i] = 16*par[j++];
+						palette[i++] += par[j++];
+						palette[i] = 16*par[j++];
+						palette[i++] += par[j];
+#endif
+						set_palette() ;
+						vc_state = ESnormal;
+					}
+				} else
+					vc_state = ESnormal;
+				continue;
+			case ESsquare:
+				for(npar = 0; npar < NPAR ; npar++)
+					par[npar] = 0;
+				npar = 0;
+				vc_state = ESgetpars;
+				if (c == '[') { /* Function key */
+					vc_state=ESfunckey;
+					continue;
+				}
+				ques = (c == '?');
+				if (ques)
+					continue;
+			case ESgetpars:
+				if(c==';' && npar<NPAR-1) {
+					npar++;
+					continue;
+				} else if (c>='0' && c<='9') {
+					par[npar] = par[npar] * 10 + c - '0';
+					continue;
+				} else vc_state=ESgotpars;
+			case ESgotpars:
+				vc_state=ESnormal;
+				switch (c) {
+					case 'h':
+						set_mode(currcons,1);
+						continue;
+					case 'l':
+						set_mode(currcons,0);
+						continue;
+					case 'n':
+						if (!ques) {
+							if (par[0] == 5)
+								status_report(tty);
+							else if (par[0] == 6)
+								cursor_report(currcons,tty);
+						}
+						continue;
+				}
+				if (ques) {
+					ques = 0;
+					continue;
+				}
+				switch(c) {
+					case 'G': case '`':
+						if (par[0]) par[0]--;
+						gotoxy(currcons,par[0],y);
+						continue;
+					case 'A':
+						if (!par[0]) par[0]++;
+						gotoxy(currcons,x,y-par[0]);
+						continue;
+					case 'B': case 'e':
+						if (!par[0]) par[0]++;
+						gotoxy(currcons,x,y+par[0]);
+						continue;
+					case 'C': case 'a':
+						if (!par[0]) par[0]++;
+						gotoxy(currcons,x+par[0],y);
+						continue;
+					case 'D':
+						if (!par[0]) par[0]++;
+						gotoxy(currcons,x-par[0],y);
+						continue;
+					case 'E':
+						if (!par[0]) par[0]++;
+						gotoxy(currcons,0,y+par[0]);
+						continue;
+					case 'F':
+						if (!par[0]) par[0]++;
+						gotoxy(currcons,0,y-par[0]);
+						continue;
+					case 'd':
+						if (par[0]) par[0]--;
+						gotoxy(currcons,x,par[0]);
+						continue;
+					case 'H': case 'f':
+						if (par[0]) par[0]--;
+						if (par[1]) par[1]--;
+						gotoxy(currcons,par[1],par[0]);
+						continue;
+					case 'J':
+						csi_J(currcons,par[0]);
+						continue;
+					case 'K':
+						csi_K(currcons,par[0]);
+						continue;
+					case 'L':
+						csi_L(currcons,par[0]);
+						continue;
+					case 'M':
+						csi_M(currcons,par[0]);
+						continue;
+					case 'P':
+						csi_P(currcons,par[0]);
+						continue;
+					case 'c':
+						if (!par[0])
+							respond_ID(tty);
+						continue;
+					case 'g':
+						if (!par[0])
+							tab_stop[x >> 5] &= ~(1 << (x & 31));
+						else if (par[0] == 3) {
+							tab_stop[0] =
+							tab_stop[1] =
+							tab_stop[2] =
+							tab_stop[3] =
+							tab_stop[4] = 0;
+						}
+						continue;
+					case 'm':
+						csi_m(currcons);
+						continue;
+					case 'q': /* DECLL - but only 3 leds */
+						/* map 0,1,2,3 to 0,1,2,4 */
+						if (par[0] < 4)
+						  setledstate(kbd_table + currcons,
+						  	      (par[0] < 3 ? par[0] : 4));
+						continue;
+					case 'r':
+						if (!par[0])
+							par[0]++;
+						if (!par[1])
+							par[1] = video_num_lines;
+						if (par[0] < par[1] &&
+						    par[1] <= video_num_lines) {
+							top=par[0]-1;
+							bottom=par[1];
+							gotoxy(currcons,0,0);
+						}
+						continue;
+					case 's':
+						save_cur(currcons);
+						continue;
+					case 'u':
+						restore_cur(currcons);
+						continue;
+					case 'X':
+						csi_X(currcons, par[0]);
+						continue;
+					case '@':
+						csi_at(currcons,par[0]);
+						continue;
+					case ']': /* setterm functions */
+						setterm_command(currcons);
+						continue;
+				}
+				continue;
+			case ESpercent:
+				vc_state = ESnormal;
+				switch (c) {
+					case '@': /* defined in ISO 2022 */
+						utf = 0;
+						continue;
+					case 'G': /* prelim official escape code */
+					case '8': /* retained for compatibility */
+						utf = 1;
+						continue;
+				}
+				continue;
+			case ESfunckey:
+				vc_state = ESnormal;
+				continue;
+			case EShash:
+				vc_state = ESnormal;
+				if (c == '8') {
+					/* DEC screen alignment test. kludge :-) */
+				}
+				continue;
+			case ESsetG0:
+				if (c == '0')
+					G0_charset = GRAF_MAP;
+				else if (c == 'B')
+					G0_charset = LAT1_MAP;
+				else if (c == 'U')
+					G0_charset = IBMPC_MAP;
+				else if (c == 'K')
+					G0_charset = USER_MAP;
+				if (charset == 0)
+					translate = set_translate(G0_charset);
+				vc_state = ESnormal;
+				continue;
+			case ESsetG1:
+				if (c == '0')
+					G1_charset = GRAF_MAP;
+				else if (c == 'B')
+					G1_charset = LAT1_MAP;
+				else if (c == 'U')
+					G1_charset = IBMPC_MAP;
+				else if (c == 'K')
+					G1_charset = USER_MAP;
+				if (charset == 1)
+					translate = set_translate(G1_charset);
+				vc_state = ESnormal;
+				continue;
+			default:
+				vc_state = ESnormal;
+		}
+	}
+	if (vcmode != KD_GRAPHICS)
+		set_cursor(currcons);
+	enable_bh(KEYBOARD_BH);
+	restore_cursors(currcons);
+	return n;
+}
+
+static int con_write_room(struct tty_struct *tty)
+{
+	if (tty->stopped)
+		return 0;
+	return 4096;		/* No limit, really; we're not buffering */
+}
+
+static int con_chars_in_buffer(struct tty_struct *tty)
+{
+	return 0;		/* we're not buffering */
+}
+
+void poke_blanked_console(void)
+{
+	timer_active&=~(1<<BLANK_TIMER);
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+		return;
+	if (console_blanked) {
+		timer_table[BLANK_TIMER].expires = 0;
+		timer_active |= 1<<BLANK_TIMER;
+	} else if(blankinterval) {
+		timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
+		timer_active |= 1<<BLANK_TIMER;
+	}
+}
+
+static void console_print(const char *b)
+{
+	int currcons = fg_console;
+	unsigned char c;
+	static int printing = 0;
+
+  if(vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+  {
+    prints(b);
+    return;
+  }
+
+	if (!printable || printing)
+		return;  /* console not yet initialized */
+	printing = 1;
+
+	if(!vc_cons_allocated(currcons)) {
+		/* impossible */
+	  	printk("console_print: tty %d not allocated ??\n",currcons+1);
+	  	return;
+	}
+
+	remove_cursors(currcons);
+
+	while ((c = *b++) != 0) {
+		if (c == 10 || c == 13 || need_wrap) {
+			if (c != 13)
+				lf(currcons);
+			cr(currcons);
+			if(c==10 || c==13)
+				continue;
+		}
+		charwrite(currcons,pos,c);
+		if (x == video_num_columns - 1) {
+			need_wrap=1;
+			continue;
+		}
+		x++;
+		pos += bytes_per_char_h;
+	}
+	set_cursor(currcons);
+
+	restore_cursors(currcons);
+
+	poke_blanked_console();
+	printing = 0;
+}
+
+/*
+ * con_throttle and con_unthrottle are only used for
+ * paste_selection(), which has to stuff in a large number of
+ * characters...
+ */
+static void con_throttle(struct tty_struct *tty)
+{
+}
+
+static void con_unthrottle(struct tty_struct *tty)
+{
+  	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+
+  	wake_up_interruptible(&vt->paste_wait);
+}
+
+static void vc_init(unsigned int currcons, unsigned long rows, unsigned long cols, int do_clear)
+{
+	long base = (long) vc_scrbuf[currcons];
+
+	video_num_columns = cols;
+	video_num_lines   = rows;
+	video_size_row    = video_num_columns*bytes_per_char_h*bytes_per_char_v;
+	video_screen_size = video_num_lines * video_size_row;
+	video_buf_size    = video_num_lines * video_num_columns * 4;
+	video_mem_start   = video_mem_base;
+	video_mem_end	  = video_mem_term;
+
+	pos = origin	  = video_mem_start;
+	scr_end		  = video_mem_start + video_num_lines * video_size_row;
+	video_mem_end     = base + video_screen_size;
+	reset_vc(currcons);
+	def_forecol	  = color_table[7];
+	def_backcol	  = color_table[0];
+	vt_cons[currcons]->paste_wait = 0;
+	cursoron	  = 0;
+
+	reset_terminal(currcons,do_clear);
+}
+
+static void con_setsize(unsigned long rows,unsigned long cols)
+{
+	video_num_lines   = rows;
+	video_num_columns = cols;
+	video_size_row    = video_num_columns*bytes_per_char_h*bytes_per_char_v;
+	video_screen_size = video_num_lines * video_size_row;
+	video_buf_size    = video_num_lines * video_num_columns * 4;
+}
+
+static int con_ioctl(struct tty_struct *tty, struct file * file,
+	     unsigned int cmd, unsigned long arg)
+{
+	return vt_ioctl(tty, file, cmd, arg);
+}
+
+/*
+ *  unsigned long con_init(unsigned long);
+ *
+ * This routine initialises console interrupts, and does nothing
+ * else. If you want the screen to clear, call tty_write with
+ * the apropriate escape-sequence.
+ *
+ */
+unsigned long con_init(unsigned long kmem_start)
+{
+	int currcons = 0, i;
+
+	memset(&console_driver, 0, sizeof(struct tty_driver));
+	console_driver.magic = TTY_DRIVER_MAGIC;
+	console_driver.name = "tty";
+	console_driver.name_base = 1;
+	console_driver.major = TTY_MAJOR;
+	console_driver.minor_start = 1;
+	console_driver.num = MAX_NR_CONSOLES;
+	console_driver.type = TTY_DRIVER_TYPE_CONSOLE;
+	console_driver.init_termios = tty_std_termios;
+	console_driver.flags = TTY_DRIVER_REAL_RAW;
+	console_driver.refcount = &console_refcount;
+	console_driver.table = console_table;
+	console_driver.termios = console_termios;
+	console_driver.termios_locked = console_termios_locked;
+
+	console_driver.open = con_open;
+	console_driver.write = con_write;
+	console_driver.write_room = con_write_room;
+	console_driver.chars_in_buffer = con_chars_in_buffer;
+	console_driver.ioctl = con_ioctl;
+	console_driver.stop = con_stop;
+	console_driver.start = con_start;
+	console_driver.throttle = con_throttle;
+	console_driver.unthrottle = con_unthrottle;
+
+	if(tty_register_driver(&console_driver))
+	  	panic("Couldn't register console driver\n");
+
+	con_setsize(ORIG_VIDEO_LINES,ORIG_VIDEO_COLS);
+
+	timer_table[BLANK_TIMER].fn=blank_screen;
+	timer_table[BLANK_TIMER].expires=0;
+	if(blankinterval) {
+  		timer_table[BLANK_TIMER].expires=jiffies+blankinterval;
+		timer_active|=1<<BLANK_TIMER;
+	}
+
+	for(i=0; i<17; i++)
+		vidc_write(i<<2, default_palette_entries[i]);
+
+	/* Use ORIG_VIDEO_MODE */
+	video_addr_mask= (video_num_lines * video_num_columns * bytes_per_char_h *
+				 bytes_per_char_v - 1) | (PAGE_SIZE-1);
+	video_mem_term = 0x02000000;
+	video_mem_base = video_mem_term - video_addr_mask - 1;
+
+	map_screen_mem (video_mem_base, 1);
+
+	can_do_color=1;
+	if(bytes_per_char_h == 8)
+	{
+		default_palette_entries = palette_8;
+		color_table = color_8;
+	}
+
+	for(currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
+		vc_cons[currcons].d = (struct vc_data *) kmem_start;
+		kmem_start += sizeof(struct vc_data);
+		vt_cons[currcons] = (struct vt_struct *) kmem_start;
+		kmem_start += sizeof(struct vt_struct);
+		vc_scrbuf[currcons] = (unsigned char *) kmem_start;
+		memfastset(vc_scrbuf[currcons],	0x00000720, video_buf_size);
+		kmem_start += video_buf_size;
+		kmalloced = 0;
+		screenbuf_size = video_buf_size;
+		vc_init(currcons, video_num_lines, video_num_columns,currcons);
+	}
+
+	currcons = fg_console = 0;
+
+	video_mem_start = video_mem_base;
+	video_mem_end	= video_mem_term;
+	pos=origin=cp	= video_mem_start;
+	scr_end		= video_mem_start + video_num_lines * video_size_row;
+	cursoron	= 1;
+
+	gotoxy (currcons,ORIG_X,ORIG_Y);
+	csi_J (currcons, 0);
+	printable=1;
+	printk ("Console: %s %s %ldx%ldx%d, %d virtual console%s (max %d)\n","colour", "A5000",
+  		video_num_columns,
+  		video_num_lines,
+  		bytes_per_char_h == 8 ? 256:16,
+  		MIN_NR_CONSOLES,
+  		(MIN_NR_CONSOLES == 1) ? "":"s",
+  		MAX_NR_CONSOLES);
+	register_console (console_print);
+	if(request_irq (IRQ_VSYNCPULSE, vsync_irq, 0, "console"))
+		panic ("Unable to get VSYNC irq for console\n");
+	return kmem_start;
+}
+
+static void get_scrmem(int currcons)
+{
+}
+
+void update_scrmem(int currcons, int start, int length)
+{
+	unsigned long p, pp, sx, sy, ex, ey;
+	unsigned long *buffer;
+
+	sy = start / video_num_columns;
+	sx = start % video_num_columns;
+	length += start;
+	ey = length / video_num_columns;
+	ex = length % video_num_columns;
+
+	if (ey > video_num_lines)
+		ey = video_num_lines;
+
+	p = origin + sy * video_size_row;
+	buffer = ((unsigned long *)vc_scrbuf[currcons]) + start;
+
+	if (ey > sy)
+	{
+		for (; sy < ey; sy++)
+		{
+			pp = p + sx * bytes_per_char_h;
+			for (; sx < video_num_columns; sx++)
+			{
+				ll_char_write (pp, buffer[0] & 0xff, (buffer[0] >> 8) & 0xff,
+					(buffer[0] >> 16) & 0xff, (buffer[0] >> 24) & 0xff);
+				pp += bytes_per_char_h;
+				buffer ++;
+			}
+			p += video_size_row;
+			sx = 0;
+		}
+	}
+	if (ey == sy && ex)
+	{
+		for (; sx < ex; sx++)
+		{
+			ll_char_write (p, buffer[0] & 0xff, (buffer[0] >> 8) & 0xff,
+				(buffer[0] >> 16) & 0xff, (buffer[0] >> 24) & 0xff);
+			p += bytes_per_char_h;
+			buffer ++;
+		}
+	}
+}
+
+void set_scrmem(int currcons,long offset)
+{
+	unsigned long p,pp,mx,my;
+	unsigned long *buffer;
+	int i;
+
+	p = origin;
+	buffer = (unsigned long *)vc_scrbuf[currcons];
+
+	for(my = 0; my < video_num_lines ; my++)
+	{
+		pp = p;
+		for(mx = 0; mx < video_num_columns; mx++)
+		{
+			ll_char_write(pp,buffer[0] & 0xff,(buffer[0] >> 8) & 0xff,
+					 (buffer[0] >> 16) & 0xff,(buffer[0] >> 24) & 0xff);
+			pp+=bytes_per_char_h;
+			buffer+=1;
+		}
+		p+=video_size_row;
+	}
+	pp = origin + ((video_screen_size + 0x7FFF) & PAGE_MASK);
+	while(p<pp)
+	{
+		*(unsigned long *)p=0;
+		p+=4;
+	}
+	if(!paletteentries || vcmode != KD_GRAPHICS)
+		for(i=0; i<17; i++)
+			vidc_write(i<<2, default_palette_entries[i]);
+	else
+		for(i=0; i<17; i++)
+			vidc_write(i<<2, paletteentries[i] & 0x1fff);
+}
+
+/* ------------------------------------------------------------------------------------- *
+ * Screen blanking routines
+ * ------------------------------------------------------------------------------------- */
+
+void do_blank_screen(int nopowersave)
+{
+	int i;
+
+	if (console_blanked)
+		return;
+
+	timer_active &= ~(1<<BLANK_TIMER);
+	timer_table[BLANK_TIMER].fn = unblank_screen;
+
+	/* DISABLE VIDEO */
+	for (i = 0; i < 17; i++)
+		vidc_write(i<<2, 0);
+
+	console_blanked = fg_console + 1;
+}
+
+void do_unblank_screen(void)
+{
+	int i;
+	int currcons;
+
+	if (!console_blanked)
+		return;
+	if (!vc_cons_allocated(fg_console)) {
+		/* impossible */
+		printk("unblank_screen: tty %d not allocated ??\n", fg_console+1);
+		return;
+	}
+	timer_table[BLANK_TIMER].fn = blank_screen;
+	if (blankinterval)
+	{
+		timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
+		timer_active |= 1<<BLANK_TIMER;
+	}
+
+	currcons = fg_console;
+	console_blanked = 0;
+
+	if(!paletteentries || vcmode != KD_GRAPHICS)
+		for(i=0; i<17; i++)
+			vidc_write(i<<2, default_palette_entries[i]);
+	else
+		for(i=0; i<17; i++)
+			vidc_write(i<<2, paletteentries[i] & 0x1fff);
+}
+
+/*
+ * If a blank_screen is due to a timer, then a power save is allowed.
+ * If it is related to console_switching, then avoid power save.
+ */
+static void blank_screen(void)
+{
+	do_blank_screen(0);
+}
+
+static void unblank_screen(void)
+{
+	do_unblank_screen();
+}
+
+void update_screen(int new_console)
+{
+	static int lock = 0;
+
+	if(new_console == fg_console || lock)
+		return;
+	if(!vc_cons_allocated(new_console)) {
+		/* strange ... */
+		printk("update_screen: tty %d not allocated ??\n",new_console);
+		return;
+	}
+	lock = 1;
+	
+	highlight_pointer(fg_console,-1);
+	
+	remove_cursors(fg_console);
+	
+	if(!console_blanked)
+		get_scrmem(fg_console);
+	else
+		console_blanked = -1;		/* no longer of the form console+1 */
+	fg_console = new_console;	/* this is the only (nonzero) assignment to fg_console */
+					/* consequently, fg_console will always be allocated */
+	set_scrmem(fg_console, 0);
+	set_origin(fg_console);
+	set_cursor(fg_console);
+	restore_cursors(fg_console);
+	set_leds();
+	compute_shiftstate();
+	lock = 0;
+}
+
+/*
+ * Allocate the console screen memory
+ */
+int con_open(struct tty_struct *tty,struct file *filp)
+{
+	unsigned int idx;
+	int i;
+	
+	idx = MINOR(tty->device) - tty->driver.minor_start;
+
+	i = vc_allocate(idx);
+	if(i)
+		return i;
+
+	vt_cons[idx]->vc_num = idx;
+	tty->driver_data = vt_cons[idx];
+
+	if(!tty->winsize.ws_row && !tty->winsize.ws_col) {
+		tty->winsize.ws_row = video_num_lines;
+		tty->winsize.ws_col = video_num_columns;
+	}
+	return 0;
+}
+
+/*
+ * PIO_FONT support
+ */
+int con_set_font (char *arg)
+{
+	return -EINVAL;
+}
+
+int con_get_font (char *arg)
+{
+	return -EINVAL;
+}
+
+void reset_palette (int currcons)
+{
+}
+
+void set_palette (void)
+{
+}
+
+/* == arm specific console code ============================================================== */
+
+int palette_getentries(int currcons, int offset, int size, unsigned long *entries)
+{
+  int i;
+
+  if(offset>18 || offset<0 || size<1)
+    return -EINVAL;
+
+  if(size>19)
+    size=19;
+
+  verify_area(VERIFY_WRITE,entries,size);
+
+  if(!paletteentries)
+    for(i=offset; i<offset+size; i++)
+      *entries++=default_palette_entries[i];
+  else
+    for(i=offset; i<offset+size; i++)
+      *entries++=(paletteentries[i] & 0x1FFF);
+
+  return size;
+}
+
+int palette_setentries(int currcons, int offset, int size, unsigned long *entries)
+{
+  int i;
+
+  if(offset>18 || offset<0 || size<1)
+    return -EINVAL;
+
+  if(size>19)
+    size=19;
+
+  verify_area(VERIFY_READ, entries, size);
+
+  if(!paletteentries)
+  {
+    paletteentries = (unsigned long *)kmalloc(sizeof(unsigned long)*19,GFP_KERNEL);
+    if(!paletteentries)
+      return -ENOMEM;
+
+    for(i=0; i<19; i++)
+      paletteentries[i]=default_palette_entries[i];
+  }
+
+  for(i=offset; i<offset+size; i++)
+    paletteentries[i]=(*entries++);
+
+  if(vcmode == KD_GRAPHICS && currcons == fg_console)
+    for(i=0; i<17; i++)
+      vidc_write(i<<2, paletteentries[i] & 0x1fff);
+
+  return size;
+}
+
+void palette_update(int currcons)
+{
+  int i;
+  if(vcmode == KD_GRAPHICS && paletteentries)
+    for(i=0; i<17; i++)
+      vidc_write(i<<2, paletteentries[i] & 0x1fff);
+  else
+    for(i=0; i<17; i++)
+      vidc_write(i<<2, default_palette_entries[i]);
+}
+
+int console_getparams(int con, unsigned long *data)
+{
+	data[0] = 0;					/* version      */
+	data[1] = video_num_columns * 8;		/* horiz pixels */
+	data[2] = video_num_lines * 8;			/* vert. pixels */
+	data[3] = bytes_per_char_h == 4 ? 4 : 8;	/* bits per pix */
+	data[4] = 4;					/* depth        */
+	return 0;
+}
+
+static void put_cursor(char on_off,unsigned long newcp)
+{
+  static char con=0;
+  unsigned long cp_p=cp;
+  int c = bytes_per_char_h == 8 ? color_table[15] : 0x11 * color_table[15];
+  int i;
+
+  if(vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+    return;
+
+  cp=newcp;
+
+  if(con!=on_off)
+  {
+    if(cp_p!=-1)
+      for(i=0;i<bytes_per_char_h;i++)
+        ((unsigned char*)cp_p)[i]^=c;
+    con=on_off;
+  }
+}
+
+static void vsync_irq(int irq, struct pt_regs *regs)
+{
+  static char cursor_flash=0;
+  int currcons = fg_console;
+
+  if(++cursor_flash==16)
+  {
+    cursor_flash=0;
+    cursor_on=cursor_on?0:1;
+    if(cursoron>0)
+      put_cursor(cursor_on,cp);
+  }
+}
+
+int do_screendump(int arg)
+{
+  char *buf=(char *)arg;
+  int l;
+  if(!suser())
+    return -EPERM;
+  l=verify_area(VERIFY_WRITE,buf,2);
+  if(l)
+    return l;
+  return -ENOSYS;
+}
+
+/* This routine reverses the highlight on s-e position */
+
+static void highlight(const int currcons, int s, int e)
+{
+	int i;
+	unsigned char *buffer = (unsigned char *)vc_scrbuf[currcons];
+
+	for(i = s; i <= e; i++)
+		buffer[4*i+3] ^= INVERSE;
+
+
+	if(currcons == fg_console)
+	{
+		unsigned long p,pp;
+		int hx,hy,hex,hey,mx,my;
+
+		buffer += (s*4);
+
+		hx = s % video_num_columns;
+		hy = s / video_num_columns;
+		hex = e % video_num_columns;
+		hey = e / video_num_columns;
+
+		p = origin + (hy * video_size_row);
+		for(my = hy; my <= hey ; my++)
+		{
+			pp = p + (hx * bytes_per_char_h);
+			for(mx = hx; mx < ((my == hey)?hex+1:video_num_columns); mx++)
+			{
+				ll_char_write(pp,buffer[0],buffer[1],buffer[2],buffer[3]);
+				pp+=bytes_per_char_h;
+				buffer+=4;
+				hx = 0;
+			}
+			p+=video_size_row;
+		}
+	}
+}
+
+static void highlight_pointer(const int currcons,const int where)
+{
+}
+
+static unsigned long inwordLut[4]={
+  0x00000000, /* control char      */
+  0x03FF0000, /* digits            */
+  0x87FFFFFE, /* uppercase and '_' */
+  0x07FFFFFE  /* lowercase         */
+};
+
+static inline int inword(const char c)
+{
+  return ( inwordLut[(c>>5)&3] >> (c&0x1F) ) & 1;
+}
+
+int sel_loadlut(const int arg)
+{
+	memcpy_fromfs(inwordLut, (unsigned long *)(arg+4), 16);
+	return 0;
+}
+
+static inline int atedge(const int p)
+{
+	return (!(p % video_num_columns) || !((p + 1) % video_num_columns));
+}
+
+/* constrain v such that l <= v <= u */
+static inline short limit(const int v, const int l, const int u)
+{
+	return (v < l) ? l : ((v > u) ? u : v);
+}
+
+int set_selection(const int arg, struct tty_struct *tty)
+{
+	unsigned short *args, xs, ys, xe, ye;
+	int sel_mode, new_sel_start, new_sel_end;
+	int ps, pe, i;
+	int currcons = fg_console;
+	char spc, *obp, *bp, *spos;
+
+	char *off = (char *)vc_scrbuf[currcons];
+
+	unblank_screen();
+	args = (unsigned short *)(arg + 1);
+	xs = limit(get_fs_word(args++) - 1, 0, video_num_columns - 1);
+	ys = limit(get_fs_word(args++) - 1, 0, video_num_lines - 1);
+	xe = limit(get_fs_word(args++) - 1, 0, video_num_columns - 1);
+	ye = limit(get_fs_word(args++) - 1, 0, video_num_lines - 1);
+	sel_mode = get_fs_word(args);
+
+	ps = ys * video_num_columns + xs;
+	pe = ye * video_num_columns + xe;
+
+	if (report_mouse && (sel_mode & 16))
+	{
+		mouse_report(currcons, tty, sel_mode & 15, xs, ys);
+		return 0;
+	}
+
+	if ( ps > pe )
+	{
+		ps ^= pe;
+		pe ^= ps;
+		ps ^= pe;
+	}
+
+	switch (sel_mode)
+	{
+		case 0: /* character-by-character selection */
+			new_sel_start = ps;
+			new_sel_end = pe;
+			break;
+		case 1:	/* word-by-word selection */
+			spc = isspace(*off + (ps << 2));
+			for (new_sel_start = ps; ; ps -= 1)
+			{
+				if ((spc && !isspace(*(off + (ps << 2)))) ||
+				    (!spc && !inword(*(off + (ps << 2)))))
+					break;
+				new_sel_start = ps;
+				if (!(ps % video_num_columns))
+					break;
+			}
+			spc = isspace(*(off + (pe << 2)));
+			for (new_sel_end = pe; ; pe += 1)
+			{
+				if ((spc && !isspace(*(off + (pe << 2)))) ||
+				    (!spc && !inword(*(off + (pe << 2)))))
+					break;
+				new_sel_end = pe;
+				if (!((pe + 1) % video_num_columns))
+					break;
+			}
+			break;
+		case 2: /* line-by-line selection */
+			new_sel_start = ps - ps % video_num_columns;
+			new_sel_end   = pe + video_num_columns - pe % video_num_columns - 1;
+			break;
+		case 3:	/* pointer highlight */
+			if (sel_cons != currcons)
+			{
+				highlight_pointer(sel_cons, -1);
+				clear_selection();
+				sel_cons = currcons;
+			}
+			highlight_pointer(sel_cons, pe);
+			return 0;
+		default:
+			return -EINVAL;
+	}
+	if(new_sel_end > new_sel_start && !atedge(new_sel_end) &&
+			isspace(*(off + (new_sel_end << 2))))
+	{
+		for (pe = new_sel_end + 1; ; pe += 1)
+		{
+			if (!isspace(*(off + (pe << 2))) || atedge(pe))
+				break;
+		}
+		if (isspace(*(off + (pe << 2))))
+			new_sel_end = pe;
+	}
+	if (sel_cons != currcons)
+	{
+		clear_selection();
+		sel_cons = currcons;
+	}
+	if(sel_start == -1)
+		highlight(sel_cons, new_sel_start, new_sel_end);
+	else if (new_sel_start == sel_start)
+	{
+		if (new_sel_end == sel_end)	/* no action required */
+			return 0;
+		else if (new_sel_end > sel_end)	/* extend to right/down */
+			highlight(sel_cons, sel_end + 1, new_sel_end);
+		else				/* contract from right/up */
+			highlight(sel_cons, new_sel_end + 1, sel_end);
+	}
+	else if (new_sel_end == sel_end)
+	{
+		if (new_sel_start < sel_start)	/* extend to left */
+			highlight(sel_cons, new_sel_start, sel_start - 1);
+		else
+			highlight(sel_cons, sel_start, new_sel_start - 1);
+	}
+	else
+	{
+		clear_selection();
+		highlight(sel_cons, new_sel_start, new_sel_end);
+	}
+	sel_start = new_sel_start;
+	sel_end = new_sel_end;
+
+	obp = bp = sel_buffer;
+
+	for (i = sel_start; i <= sel_end; i+=1)
+	{
+		spos = (char *)off + (i << 2);
+		*bp++ = *spos;
+		if (!isspace(*spos))
+			obp = bp;
+		if (! ((i+1) % video_num_columns))
+		{
+			 /* strip trailing spaces from line and add newline,
+			    unless non-space at end of line. */
+			 if (obp != bp)
+			 {
+			 	bp = obp;
+			 	*bp++ = '\r';
+			 }
+			 obp = bp;
+		}
+		/* check for space, leaving room for next character, possible
+		   newline, and null at end. */
+		if (bp - sel_buffer > SEL_BUFFER_SIZE - 3)
+			break;
+	}
+	*bp = '\0';
+	return 0;
+}
+
+int paste_selection(struct tty_struct *tty)
+{
+	struct wait_queue wait;
+	char *bp = sel_buffer;
+	int c,l;
+	struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
+
+	if(!sel_buffer[0])
+		return 0;
+	unblank_screen();
+	c = strlen(sel_buffer);
+	current->state = TASK_INTERRUPTIBLE;
+	add_wait_queue(&vt->paste_wait, &wait);
+	while (c) {
+		if (test_bit(TTY_THROTTLED, &tty->flags)) {
+			schedule();
+			continue;
+		}
+		l = MIN(c, tty->ldisc.receive_room(tty));
+		tty->ldisc.receive_buf(tty,(unsigned char *) bp, 0, l);
+		c -= l;
+		bp += l;
+	}
+	current->state = TASK_RUNNING;
+	return 0;
+}
+
+static void clear_selection()
+{
+	highlight_pointer(sel_cons, -1); /* hide the pointer */
+	if (sel_start != -1)
+	{
+		highlight(sel_cons, sel_start, sel_end);
+		sel_start = -1;
+	}
+}
+
+/* -------------------------------------------------------------------------------
+ * erasing
+ * ------------------------------------------------------------------------------- */
+
+static void ll_erasebuf(int currcons,unsigned char sx,unsigned char sy,
+			unsigned char cx,unsigned char cy)
+{
+	unsigned char *buffer = vc_scrbuf[currcons];
+	if(sx!=0) /* Erase to end of line */
+	{
+		register unsigned char c;
+		if(cy!=0)
+			c=video_num_columns-sx;
+		else
+			c=video_num_columns-sx<cx?video_num_columns-sx:cx;
+		memfastset((void*)(buffer+4*(sx+sy*video_num_columns)),
+			(backcol<<16) | (forecol<<8) | 32, 4*c);
+		cx-=c;
+		sy++;
+		if(cy>0)
+			cy--;
+		sx=0;
+	}
+	if(cy!=0)
+	{
+		memfastset((void*)(buffer+sy*video_num_columns*4),
+			(backcol<<16) | (forecol<<8) | 32,cy*video_num_columns*4);
+	}
+	if(cx!=0)
+	{
+		memfastset((void*)(buffer+(sy+cy)*video_num_columns*4),
+			(backcol<<16) | (forecol<<8) | 32,cx*4);
+	}
+}
+
+static void ll_erase(int currcons,unsigned char sx,unsigned char sy,
+                      unsigned char cx,unsigned char cy)
+{
+	register unsigned char *p,cc;
+	register unsigned int i1,i2;
+
+	ll_erasebuf(currcons,sx,sy,cx,cy);
+
+	if(currcons==fg_console)
+	{
+		cc=0x11*backcol;
+
+		if(sx!=0) /* Erase to end of line */
+		{
+			register unsigned char c;
+			if(cy!=0)
+				c=video_num_columns-sx;
+			else
+				c=video_num_columns-sx<cx?video_num_columns-sx:cx;
+			p=(unsigned char*)(origin+sy*video_num_columns*
+						bytes_per_char_h*bytes_per_char_v+
+								sx*bytes_per_char_h);
+			for(i1=0;i1<c*bytes_per_char_h;i1++)
+				for(i2=0;i2<bytes_per_char_v;i2++)
+					p[i1+i2*video_num_columns*bytes_per_char_h]=cc;
+			cx-=c;
+			if(cy>0)
+				cy--;
+			sy++;
+			sx=0;
+		}
+		if(cy!=0)
+		{
+			memfastset((void*)(origin+sy*video_num_columns*
+						bytes_per_char_h*bytes_per_char_v),
+				0x11111111L*backcol,
+				cy*bytes_per_char_h*bytes_per_char_v*video_num_columns);
+		}
+		if(cx!=0)
+		{
+			p=(unsigned char*)(origin+(sy+cy)*video_num_columns*
+						bytes_per_char_h*bytes_per_char_v);
+			for(i1=0;i1<cx*bytes_per_char_h;i1++)
+				for(i2=0;i2<bytes_per_char_v;i2++)
+					p[i1+i2*video_num_columns*bytes_per_char_h]=cc;
+		}
+	}
+}
+
diff -urNwbB linux/arch/arm/drivers/char/defkeymap.c linux.arm/arch/arm/drivers/char/defkeymap.c
--- linux/arch/arm/drivers/char/defkeymap.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/defkeymap.c	Sun Mar  3 12:31:45 1996
@@ -0,0 +1,337 @@
+/*
+ * linux/arch/arm/drivers/char/defkeymap.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+/* Normal (maps 1:1 with no processing) */
+#define KTn	0xF0
+/* Function keys */
+#define KTf	0xF1
+/* Special (Performs special house-keeping funcs) */
+#define KTs	0xF2
+#define KIGNORE		K(KTs, 0)	/* Ignore */
+#define KENTER		K(KTs, 1)	/* Enter */
+#define KREGS		K(KTs, 2)	/* Regs */
+#define KMEM		K(KTs, 3)	/* Mem */
+#define KSTAT		K(KTs, 4)	/* State */
+#define KINTR		K(KTs, 5)	/* Intr */
+#define Ksl	6	/* Last console */
+#define KCAPSLK		K(KTs, 7)	/* Caps lock */
+#define KNUMLK		K(KTs, 8)	/* Num-lock */
+#define KSCRLLK		K(KTs, 9)	/* Scroll-lock */
+#define KSCRLFOR	K(KTs,10)	/* Scroll forward */
+#define KSCRLBAK	K(KTs,11)	/* Scroll back */
+#define KREBOOT		K(KTs,12)	/* Reboot */
+#define KCAPSON		K(KTs,13)	/* Caps on */
+#define KCOMPOSE	K(KTs,14)	/* Compose */
+#define KSAK		K(KTs,15)	/* SAK */
+#define CONS_DEC	K(KTs,16)	/* Dec console */
+#define CONS_INC	K(KTs,17)	/* Incr console */
+#define KFLOPPY		K(KTs,18)	/* Floppy */
+/* Key pad (0-9 = digits, 10=+, 11=-, 12=*, 13=/, 14=enter, 16=., 17=# */
+#define KTp	0xF3
+#define KPAD_0		K(KTp, 0 )
+#define KPAD_1  	K(KTp, 1 )
+#define KPAD_2		K(KTp, 2 )
+#define KPAD_3		K(KTp, 3 )
+#define KPAD_4		K(KTp, 4 )
+#define KPAD_5		K(KTp, 5 )
+#define KPAD_6		K(KTp, 6 )
+#define KPAD_7		K(KTp, 7 )
+#define KPAD_8		K(KTp, 8 )
+#define KPAD_9		K(KTp, 9 )
+#define KPAD_PL		K(KTp,10 )
+#define KPAD_MI		K(KTp,11 )
+#define KPAD_ML		K(KTp,12 )
+#define KPAD_DV		K(KTp,13 )
+#define KPAD_EN		K(KTp,14 )
+#define KPAD_DT		K(KTp,16 )
+#define KPAD_HS		K(KTp,18 )
+/* Console switching */
+#define KCn	0xF5
+/* Cursor */
+#define KTc	0xF6
+#define Kcd	0	/* Cursor down */
+#define Kcl	1	/* Cursor left */
+#define Kcr	2	/* Cursor right */
+#define Kcu	3	/* Cursor up */
+/* Shift/alt modifiers etc */
+#define KMd	0xF7
+#define KSHIFT		K(KMd, 0 )
+#define KALTGR		K(KMd, 1 )
+#define KCTRL		K(KMd, 2 )
+#define KALT		K(KMd, 3 )
+/* Meta */
+#define KMt	0xF8
+#define KAs	0xF9
+#define KPADA_0		K(KAs, 0 )
+#define KPADA_1		K(KAs, 1 )
+#define KPADA_2		K(KAs, 2 )
+#define KPADA_3		K(KAs, 3 )
+#define KPADA_4		K(KAs, 4 )
+#define KPADA_5		K(KAs, 5 )
+#define KPADA_6		K(KAs, 6 )
+#define KPADA_7		K(KAs, 7 )
+#define KPADA_8		K(KAs, 8 )
+#define KPADA_9		K(KAs, 9 )
+#define KPADB_0		K(KAs,10 )
+#define KPADB_1		K(KAs,11 )
+#define KPADB_2		K(KAs,12 )
+#define KPADB_3		K(KAs,13 )
+#define KPADB_4		K(KAs,14 )
+#define KPADB_5		K(KAs,15 )
+#define KPADB_6		K(KAs,16 )
+#define KPADB_7		K(KAs,17 )
+#define KPADB_8		K(KAs,18 )
+#define KPADB_9		K(KAs,19 )
+/* Locking keys */
+#define KLk	0xFA
+/* Letters */
+#define KTl	0xFB
+
+u_short plain_map[]=
+{
+  K(KTn, 27),K(KTf,  0),K(KTf,  1),K(KTf,  2 ),K(KTf,  3),K(KTf,  4),K(KTf,  5 ),K(KTf,  6),
+  K(KTf,  7),K(KTf,  8),K(KTf,  9),K(KTf, 10 ),K(KTf, 11),KIGNORE   ,KSCRLLK    ,KINTR     ,
+  K(KTn,'`'),K(KTn,'1'),K(KTn,'2'),K(KTn,'3' ),K(KTn,'4'),K(KTn,'5'),K(KTn,'6' ),K(KTn,'7'),
+  K(KTn,'8'),K(KTn,'9'),K(KTn,'0'),K(KTn,'-' ),K(KTn,'='),K(KTn,'£'),K(KTn,127 ),K(KTf,21 ),
+  K(KTf,20 ),K(KTf,24 ),KNUMLK    ,KPAD_DV    ,KPAD_ML   ,KPAD_HS   ,K(KTn,  9 ),K(KTl,'q'),
+  K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
+  K(KTl,'p'),K(KTn,'['),K(KTn,']'),K(KTn,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25  ),KPAD_7    ,
+  KPAD_8    ,KPAD_9    ,KPAD_MI   ,KCTRL      ,K(KTl,'a'),K(KTl,'s'),K(KTl,'d' ),K(KTl,'f'),
+  K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),K(KTn,';'),K(KTn,'\''),KENTER    ,
+  KPAD_4    ,KPAD_5    ,KPAD_6    ,KPAD_PL    ,KSHIFT    ,KIGNORE   ,K(KTl,'z' ),K(KTl,'x'),
+  K(KTl,'c'),K(KTl,'v'),K(KTl,'b'),K(KTl,'n' ),K(KTl,'m'),K(KTn,','),K(KTn,'.' ),K(KTn,'/'),
+  KSHIFT    ,K(KTc,Kcu),KPAD_1    ,KPAD_2     ,KPAD_3    ,KCAPSLK   ,KALT       ,K(KTn,' '),
+  KALTGR    ,KCTRL     ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0    ,KPAD_DT    ,KPAD_EN   
+};
+
+u_short shift_map[]=
+{
+  K(KTn, 27),K(KTf, 10),K(KTf, 11),K(KTf, 12 ),K(KTf, 13),K(KTf, 14),K(KTf, 15 ),K(KTf, 16),
+  K(KTf, 17),K(KTf, 18),K(KTf, 19),K(KTf, 20 ),K(KTf, 21),KIGNORE   ,KMEM       ,KINTR     ,
+  K(KTn,'~'),K(KTn,'!'),K(KTn,'@'),K(KTn,'#' ),K(KTn,'$'),K(KTn,'%'),K(KTn,'^' ),K(KTn,'&'),
+  K(KTn,'*'),K(KTn,'('),K(KTn,')'),K(KTn,'_' ),K(KTn,'+'),K(KTn,'¤'),K(KTn,127 ),K(KTf,21 ),
+  K(KTf,20 ),KSCRLBAK  ,KNUMLK    ,KPAD_DV    ,KPAD_ML   ,KPAD_HS   ,K(KTn,  9 ),K(KTl,'Q'),
+  K(KTl,'W'),K(KTl,'E'),K(KTl,'R'),K(KTl,'T' ),K(KTl,'Y'),K(KTl,'U'),K(KTl,'I' ),K(KTl,'O'),
+  K(KTl,'P'),K(KTn,'{'),K(KTn,'}'),K(KTn,'|' ),K(KTf,22 ),K(KTf,23 ),KSCRLFOR   ,KPAD_7    ,
+  KPAD_8    ,KPAD_9    ,KPAD_MI   ,KCTRL      ,K(KTl,'A'),K(KTl,'S'),K(KTl,'D' ),K(KTl,'F'),
+  K(KTl,'G'),K(KTl,'H'),K(KTl,'J'),K(KTl,'K' ),K(KTl,'L'),K(KTn,':'),K(KTn,'"' ),KENTER    ,
+  KPAD_4    ,KPAD_5    ,KPAD_6    ,KPAD_PL    ,KSHIFT    ,KIGNORE   ,K(KTl,'Z' ),K(KTl,'X'),
+  K(KTl,'C'),K(KTl,'V'),K(KTl,'B'),K(KTl,'N' ),K(KTl,'M'),K(KTn,'<'),K(KTn,'>' ),K(KTn,'?'),
+  KSHIFT    ,K(KTc,Kcu),KPAD_1    ,KPAD_2     ,KPAD_3    ,KCAPSLK   ,KALT       ,K(KTn,' '),
+  KALTGR    ,KCTRL     ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0    ,KPAD_DT    ,KPAD_EN   
+};
+
+u_short altgr_map[]=
+{
+  KIGNORE   ,K(KCn,12 ),K(KCn,13 ),K(KCn,14  ),K(KCn,15 ),K(KCn,16 ),K(KCn,17  ),K(KCn, 18),
+  K(KCn, 19),K(KCn,20 ),K(KCn,21 ),K(KCn,22  ),K(KCn,23 ),KIGNORE   ,KREGS      ,KINTR     ,
+  KIGNORE   ,KIGNORE   ,K(KTn,'@'),KIGNORE    ,K(KTn,'$'),KIGNORE   ,KIGNORE    ,K(KTn,'{'),
+  K(KTn,'['),K(KTn,']'),K(KTn,'}'),K(KTn,'\\'),KIGNORE   ,KIGNORE   ,KIGNORE    ,K(KTf,21 ),
+  K(KTf,20 ),K(KTf,24 ),KNUMLK    ,KPAD_DV    ,KPAD_ML   ,KPAD_HS   ,KIGNORE    ,K(KTl,'q'),
+  K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
+  K(KTl,'p'),KIGNORE   ,K(KTn,'~'),KIGNORE    ,K(KTf,22 ),K(KTf,23 ),K(KTf,25  ),KPADB_7   ,
+  KPADB_8   ,KPADB_9   ,KPAD_MI   ,KCTRL      ,K(KAs,20 ),K(KTl,'s'),K(KAs,23  ),K(KAs,25 ),
+  K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),KIGNORE   ,KIGNORE    ,KENTER    ,
+  KPADB_4   ,KPADB_5   ,KPADB_6   ,KPAD_PL    ,KSHIFT    ,KIGNORE   ,K(KTl,'z' ),K(KTl,'x'),
+  K(KAs,22 ),K(KTl,'v'),K(KTl,21 ),K(KTl,'n' ),K(KTl,'m'),KIGNORE   ,KIGNORE    ,KIGNORE   ,
+  KSHIFT    ,K(KTc,Kcu),KPADB_1   ,KPADB_2    ,KPADB_3   ,KCAPSLK   ,KALT       ,KIGNORE   ,
+  KALTGR    ,KCTRL     ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPADB_0   ,KPAD_DT    ,KPAD_EN
+};
+
+u_short ctrl_map[]=
+{
+  KIGNORE   ,K(KTf,  0),K(KTf,  1),K(KTf,  2 ),K(KTf,  3),K(KTf,  4),K(KTf, 5  ),K(KTf,  6),
+  K(KTf,  7),K(KTf,  8),K(KTf,  9),K(KTf, 10 ),K(KTf, 11),KIGNORE   ,KSTAT      ,KINTR     ,
+  KIGNORE   ,K(KTn, 1 ),K(KTn, 2 ),K(KTn, 3  ),K(KTn, 4 ),K(KTn, 5 ),K(KTn, 6  ),K(KTn, 7 ),
+  K(KTn, 8 ),K(KTn, 9 ),K(KTn, 0 ),K(KTn,31  ),KIGNORE   ,KIGNORE   ,K(KTn, 8  ),K(KTf,21 ),
+  K(KTf,20 ),K(KTf,24 ),KNUMLK    ,KPAD_DV    ,KPAD_ML   ,KPAD_HS   ,KIGNORE    ,K(KTn,17 ),
+  K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20  ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9  ),K(KTn,15 ),
+  K(KTn,16 ),K(KTn,27 ),K(KTn,29 ),K(KTn,28  ),K(KTf,22 ),K(KTf,23 ),K(KTf,25  ),KPAD_7    ,
+  KPAD_8    ,KPAD_9    ,KPAD_MI   ,KCTRL      ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4  ),K(KTn, 6 ),
+  K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11  ),K(KTn,12 ),KIGNORE   ,K(KTn, 7  ),KENTER    ,
+  KPAD_4    ,KPAD_5    ,KPAD_6    ,KPAD_PL    ,KSHIFT    ,KIGNORE   ,K(KTn,26  ),K(KTn,24 ),
+  K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14  ),K(KTn,13 ),KIGNORE   ,KCOMPOSE   ,K(KTn,127),
+  KSHIFT    ,K(KTc,Kcu),KPAD_1    ,KPAD_2     ,KPAD_3    ,KCAPSLK   ,KALT       ,K(KTn, 0 ),
+  KALTGR    ,KCTRL     ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0    ,KPAD_DT    ,KPAD_EN   
+};
+
+u_short shift_ctrl_map[]=
+{
+  KIGNORE   ,KIGNORE   ,KIGNORE   ,KIGNORE    ,KIGNORE   ,KIGNORE   ,KIGNORE    ,KIGNORE   ,
+  KIGNORE   ,KIGNORE   ,KIGNORE   ,KIGNORE    ,KIGNORE   ,KIGNORE   ,KFLOPPY    ,KINTR     ,
+  KIGNORE   ,KIGNORE   ,K(KTn, 0 ),KIGNORE    ,KIGNORE   ,KIGNORE   ,KIGNORE    ,KIGNORE   ,
+  KIGNORE   ,KIGNORE   ,KIGNORE   ,K(KTn,31  ),KIGNORE   ,KIGNORE   ,KIGNORE    ,K(KTf,21 ),
+  K(KTf,20 ),K(KTf,24 ),KNUMLK    ,KPAD_DV    ,KPAD_ML   ,KPAD_HS   ,KIGNORE    ,K(KTn,17 ),
+  K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20  ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9  ),K(KTn,15 ),
+  K(KTn,16 ),KIGNORE   ,KIGNORE   ,KIGNORE    ,K(KTf,22 ),K(KTf,23 ),K(KTf,25  ),KPAD_7    ,
+  KPAD_8    ,KPAD_9    ,KPAD_MI   ,KCTRL      ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4  ),K(KTn, 6 ),
+  K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11  ),K(KTn,12 ),KIGNORE   ,K(KTn, 7  ),KENTER    ,
+  KPAD_4    ,KPAD_5    ,KPAD_6    ,KPAD_PL    ,KSHIFT    ,KIGNORE   ,K(KTn,26  ),K(KTn,24 ),
+  K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14  ),K(KTn,13 ),KIGNORE   ,KIGNORE    ,KIGNORE   ,
+  KSHIFT    ,K(KTc,Kcu),KPAD_1    ,KPAD_2     ,KPAD_3    ,KCAPSLK   ,KALT       ,K(KTn, 0 ),
+  KALTGR    ,KCTRL     ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0    ,KPAD_DT    ,KPAD_EN   
+};
+
+u_short alt_map[]=
+{
+  K(KMt,27 ),K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2  ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5  ),K(KCn, 6 ),
+  K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10  ),K(KCn,11 ),KIGNORE   ,KSCRLLK    ,KINTR     ,
+  K(KMt,'`'),K(KMt,'1'),K(KMt,'2'),K(KMt,'3' ),K(KMt,'4'),K(KMt,'5'),K(KMt,'6' ),K(KMt,'7'),
+  K(KMt,'8'),K(KMt,'9'),K(KMt,'0'),K(KMt,'-' ),K(KMt,'='),K(KMt,'£'),K(KMt,127 ),K(KTf,21 ),
+  K(KTf,20 ),K(KTf,24 ),KNUMLK    ,KPAD_DV    ,KPAD_ML   ,KPAD_HS   ,K(KMt, 9  ),K(KMt,'q'),
+  K(KMt,'w'),K(KMt,'e'),K(KMt,'r'),K(KMt,'t' ),K(KMt,'y'),K(KMt,'u'),K(KMt,'i' ),K(KMt,'o'),
+  K(KMt,'p'),K(KMt,'['),K(KMt,']'),K(KMt,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25  ),KPADA_7   ,
+  KPADA_8   ,KPADA_9   ,KPAD_MI   ,KCTRL      ,K(KMt,'a'),K(KMt,'s'),K(KMt,'d' ),K(KMt,'f'),
+  K(KMt,'g'),K(KMt,'h'),K(KMt,'j'),K(KMt,'k' ),K(KMt,'l'),K(KMt,';'),K(KMt,'\''),K(KMt,13 ),
+  KPADA_4   ,KPADA_5   ,KPADA_6   ,KPAD_PL    ,KSHIFT    ,KIGNORE   ,K(KMt,'z' ),K(KMt,'x'),
+  K(KMt,'c'),K(KMt,'v'),K(KMt,'b'),K(KMt,'n' ),K(KMt,'m'),K(KMt,','),K(KMt,'.' ),KIGNORE   ,
+  KSHIFT    ,K(KTc,Kcu),KPADA_1   ,KPADA_2    ,KPADA_3   ,KCAPSLK   ,KALT       ,K(KMt,' '),
+  KALTGR    ,KCTRL     ,CONS_DEC  ,K(KTc,Kcd ),CONS_INC  ,KPADA_0   ,KPAD_DT    ,KPAD_EN
+};
+
+u_short ctrl_alt_map[]=
+{
+  KIGNORE   ,K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2  ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5  ),K(KCn, 6 ),
+  K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10  ),K(KCn,11 ),KIGNORE   ,KIGNORE    ,KINTR     ,
+  KIGNORE   ,KIGNORE   ,KIGNORE   ,KIGNORE    ,KIGNORE   ,KIGNORE   ,KIGNORE    ,KIGNORE   ,
+  KIGNORE   ,KIGNORE   ,KIGNORE   ,KIGNORE    ,KIGNORE   ,KIGNORE   ,KIGNORE    ,K(KTf,21 ),
+  K(KTf,20 ),K(KTf,24 ),KNUMLK    ,KPAD_DV    ,KPAD_ML   ,KPAD_HS   ,KIGNORE    ,K(KMt,17 ),
+  K(KMt,23 ),K(KMt, 5 ),K(KMt,18 ),K(KMt,20  ),K(KMt,25 ),K(KMt,21 ),K(KMt, 9  ),K(KMt,15 ),
+  K(KMt,16 ),KIGNORE   ,KIGNORE   ,KIGNORE    ,KREBOOT   ,K(KTf,23 ),K(KTf,25  ),KPAD_7    ,
+  KPAD_8    ,KPAD_9    ,KPAD_MI   ,KCTRL      ,K(KMt, 1 ),K(KMt,19 ),K(KMt, 4  ),K(KMt, 6 ),
+  K(KMt, 7 ),K(KMt, 8 ),K(KMt,10 ),K(KMt,11  ),K(KMt,12 ),KIGNORE   ,KIGNORE    ,KENTER    ,
+  KPAD_4    ,KPAD_5    ,KPAD_6    ,KPAD_PL    ,KSHIFT    ,KIGNORE   ,K(KMt,26  ),K(KMt,24 ),
+  K(KMt, 3 ),K(KMt,22 ),K(KMt, 2 ),K(KMt,14  ),K(KMt,13 ),KIGNORE   ,KIGNORE    ,KIGNORE   ,
+  KSHIFT    ,K(KTc,Kcu),KPAD_1    ,KPAD_2     ,KPAD_3    ,KCAPSLK   ,KALT       ,KIGNORE   ,
+  KALTGR    ,KCTRL     ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0    ,KREBOOT    ,KPAD_EN
+};
+
+ushort *key_maps[MAX_NR_KEYMAPS] = {
+	plain_map, shift_map, altgr_map, 0,
+	ctrl_map, shift_ctrl_map, 0, 0,
+	alt_map, 0, 0, 0,
+	ctrl_alt_map,	0
+};
+
+unsigned int keymap_count = 7;
+
+/*
+ * Philosophy: most people do not define more strings, but they who do
+ * often want quite a lot of string space. So, we statically allocate
+ * the default and allocate dynamically in chunks of 512 bytes.
+ */
+
+char func_buf[] = {
+	'\033', '[', '[', 'A', 0,
+	'\033', '[', '[', 'B', 0,
+	'\033', '[', '[', 'C', 0,
+	'\033', '[', '[', 'D', 0,
+	'\033', '[', '[', 'E', 0,
+	'\033', '[', '1', '7', '~', 0,
+	'\033', '[', '1', '8', '~', 0,
+	'\033', '[', '1', '9', '~', 0,
+	'\033', '[', '2', '0', '~', 0,
+	'\033', '[', '2', '1', '~', 0,
+	'\033', '[', '2', '3', '~', 0,
+	'\033', '[', '2', '4', '~', 0,
+	'\033', '[', '2', '5', '~', 0,
+	'\033', '[', '2', '6', '~', 0,
+	'\033', '[', '2', '8', '~', 0,
+	'\033', '[', '2', '9', '~', 0,
+	'\033', '[', '3', '1', '~', 0,
+	'\033', '[', '3', '2', '~', 0,
+	'\033', '[', '3', '3', '~', 0,
+	'\033', '[', '3', '4', '~', 0,
+	'\033', '[', '1', '~', 0,
+	'\033', '[', '2', '~', 0,
+	'\033', '[', '3', '~', 0,
+	'\033', '[', '4', '~', 0,
+	'\033', '[', '5', '~', 0,
+	'\033', '[', '6', '~', 0,
+	'\033', '[', 'M', 0,
+	'\033', '[', 'P', 0,
+};
+
+char *funcbufptr = func_buf;
+int funcbufsize = sizeof(func_buf);
+int funcbufleft = 0;          /* space left */
+
+char *func_table[MAX_NR_FUNC] = {
+	func_buf + 0,
+	func_buf + 5,
+	func_buf + 10,
+	func_buf + 15,
+	func_buf + 20,
+	func_buf + 25,
+	func_buf + 31,
+	func_buf + 37,
+	func_buf + 43,
+	func_buf + 49,
+	func_buf + 55,
+	func_buf + 61,
+	func_buf + 67,
+	func_buf + 73,
+	func_buf + 79,
+	func_buf + 85,
+	func_buf + 91,
+	func_buf + 97,
+	func_buf + 103,
+	func_buf + 109,
+	func_buf + 115,
+	func_buf + 120,
+	func_buf + 125,
+	func_buf + 130,
+	func_buf + 135,
+	func_buf + 140,
+	func_buf + 145,
+	0,
+	0,
+	func_buf + 149,
+	0,
+};
+
+struct kbdiacr accent_table[MAX_DIACR] = {
+	{'`', 'A', '\300'},	{'`', 'a', '\340'},
+	{'\'', 'A', '\301'},	{'\'', 'a', '\341'},
+	{'^', 'A', '\302'},	{'^', 'a', '\342'},
+	{'~', 'A', '\303'},	{'~', 'a', '\343'},
+	{'"', 'A', '\304'},	{'"', 'a', '\344'},
+	{'O', 'A', '\305'},	{'o', 'a', '\345'},
+	{'0', 'A', '\305'},	{'0', 'a', '\345'},
+	{'A', 'A', '\305'},	{'a', 'a', '\345'},
+	{'A', 'E', '\306'},	{'a', 'e', '\346'},
+	{',', 'C', '\307'},	{',', 'c', '\347'},
+	{'`', 'E', '\310'},	{'`', 'e', '\350'},
+	{'\'', 'E', '\311'},	{'\'', 'e', '\351'},
+	{'^', 'E', '\312'},	{'^', 'e', '\352'},
+	{'"', 'E', '\313'},	{'"', 'e', '\353'},
+	{'`', 'I', '\314'},	{'`', 'i', '\354'},
+	{'\'', 'I', '\315'},	{'\'', 'i', '\355'},
+	{'^', 'I', '\316'},	{'^', 'i', '\356'},
+	{'"', 'I', '\317'},	{'"', 'i', '\357'},
+	{'-', 'D', '\320'},	{'-', 'd', '\360'},
+	{'~', 'N', '\321'},	{'~', 'n', '\361'},
+	{'`', 'O', '\322'},	{'`', 'o', '\362'},
+	{'\'', 'O', '\323'},	{'\'', 'o', '\363'},
+	{'^', 'O', '\324'},	{'^', 'o', '\364'},
+	{'~', 'O', '\325'},	{'~', 'o', '\365'},
+	{'"', 'O', '\326'},	{'"', 'o', '\366'},
+	{'/', 'O', '\330'},	{'/', 'o', '\370'},
+	{'`', 'U', '\331'},	{'`', 'u', '\371'},
+	{'\'', 'U', '\332'},	{'\'', 'u', '\372'},
+	{'^', 'U', '\333'},	{'^', 'u', '\373'},
+	{'"', 'U', '\334'},	{'"', 'u', '\374'},
+	{'\'', 'Y', '\335'},	{'\'', 'y', '\375'},
+	{'T', 'H', '\336'},	{'t', 'h', '\376'},
+	{'s', 's', '\337'},	{'"', 'y', '\377'},
+	{'s', 'z', '\337'},	{'i', 'j', '\377'},
+};
+
+unsigned int accent_table_size = 68;
diff -urNwbB linux/arch/arm/drivers/char/diacr.h linux.arm/arch/arm/drivers/char/diacr.h
--- linux/arch/arm/drivers/char/diacr.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/diacr.h	Sun Feb 11 19:43:56 1996
@@ -0,0 +1,8 @@
+#ifndef _DIACR_H
+#define _DIACR_H
+#include <linux/kd.h>
+
+extern struct kbdiacr accent_table[];
+extern unsigned int accent_table_size;
+
+#endif /* _DIACR_H */
diff -urNwbB linux/arch/arm/drivers/char/iic.c linux.arm/arch/arm/drivers/char/iic.c
--- linux/arch/arm/drivers/char/iic.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/iic.c	Sun Mar  3 12:33:06 1996
@@ -0,0 +1,137 @@
+/*
+ * linux/arch/arm/drivers/char/iic.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ *
+ * IIC is used to get the current time from the CMOS rtc.
+ */
+
+#include <asm/system.h>
+#include <asm/delay.h>
+#include <asm/io.h>
+
+extern unsigned char volatile *const ioc;
+
+#define DELAY udelay(10)
+
+static void iic_start (void)
+{
+    unsigned char out;
+
+    out = ioc[0] | 0xc2;
+
+    ioc[0] = out;
+    DELAY;
+
+    ioc[0] = out ^ 1;
+    DELAY;
+}
+
+static void iic_stop (void)
+{
+    unsigned char out;
+
+    out = ioc[0] | 0xc3;
+
+    DELAY;
+    ioc[0] = out ^ 1;
+
+    DELAY;
+    ioc[0] = out;
+}
+
+static int iic_sendbyte (unsigned char b)
+{
+    unsigned char out, in;
+    int i;
+
+    out = (ioc[0] & 0xFC) | 0xC0;
+
+    ioc[0] = out;
+    for (i = 7; i >= 0; i--) {
+	ioc[0] = out | ((b & (1 << i)) ? 1 : 0);
+	DELAY;
+
+	ioc[0] = out | ((b & (1 << i)) ? 1 : 0) | 2;
+	DELAY;
+
+	ioc[0] = out | ((b & (1 << i)) ? 1 : 0);
+    }
+    ioc[0] = out | 1;
+    DELAY;
+
+    ioc[0] = out | 3;
+    DELAY;
+
+    in = ioc[0] & 1;
+
+    ioc[0] = out | 1;
+    DELAY;
+
+    ioc[0] = out;
+    DELAY;
+
+    if(in) {
+	printk("No acknowledge from RTC\n");
+	return 1;
+    } else
+	return 0;
+}
+
+static unsigned char iic_recvbyte (void)
+{
+    unsigned char out, in;
+    int i;
+
+    out = (ioc[0] & 0xFC) | 0xC0;
+
+    ioc[0] = out;
+    in = 0;
+    for (i = 7; i >= 0; i--) {
+	ioc[0] = out | 1;
+	DELAY;
+
+	ioc[0] = out | 3;
+	DELAY;
+
+	in = (in << 1) | (ioc[0] & 1);
+	ioc[0] = out | 1;
+	DELAY;
+    }
+    ioc[0] = out;
+    DELAY;
+
+    ioc[0] = out | 2;
+    DELAY;
+
+    return in;
+}
+
+void iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
+{
+    iic_start();
+
+    if (iic_sendbyte(addr & 0xfe))
+	goto error;
+
+    if (iic_sendbyte(loc))
+	goto error;
+
+    if (addr & 1) {
+	int i;
+
+	for (i = 0; i < len; i++)
+	    if (iic_sendbyte (buf[i]))
+		break;
+    } else {
+	int i;
+
+	iic_stop();
+	iic_start();
+	iic_sendbyte(addr|1);
+	for (i = 0; i < len; i++)
+	    buf[i] = iic_recvbyte ();
+    }
+error:
+    iic_stop();
+}
diff -urNwbB linux/arch/arm/drivers/char/kbdmouse.c linux.arm/arch/arm/drivers/char/kbdmouse.c
--- linux/arch/arm/drivers/char/kbdmouse.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/kbdmouse.c	Sun Mar  3 12:39:09 1996
@@ -0,0 +1,188 @@
+/*
+ * linux/arch/arm/drivers/char/kbdmouse.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ *
+ * Medium-level interface for quadrature mouse connected to the keyboard.
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+
+char kernel_version[] = UTS_RELEASE;
+#define arch_mouse_init init_module
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mouse.h>
+#include <linux/random.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+char   mouse_buttons;
+int    mouse_dxpos;
+int    mouse_dypos;
+int    mouse_present;
+char   mouse_ready;
+char   mouse_active;
+struct wait_queue *mouse_wait;
+struct fasync_struct *fasyncptr;
+
+static int fasync_mouse(struct inode *inode, struct file *filp, int on)
+{
+    int retval;
+
+    retval = fasync_helper(inode, filp, on, &fasyncptr);
+    if (retval < 0)
+	return retval;
+    return 0;
+}
+
+static void close_mouse(struct inode *inode, struct file *file)
+{
+    fasync_mouse (inode, file, 0);
+    if (--mouse_active)
+	return;
+    mouse_ready = 0;
+
+    MOD_DEC_USE_COUNT;
+}
+
+static int open_mouse(struct inode *inode,struct file *file)
+{
+    unsigned long flags;
+
+    if (!mouse_present)
+	return -EINVAL;
+    if (mouse_active++)
+	return 0;
+
+    save_flags (flags);
+    cli ();
+    mouse_active  = 1;
+    mouse_ready   = 0;
+    mouse_dxpos   = 0;
+    mouse_dypos   = 0;
+    mouse_buttons = 0;
+    restore_flags (flags);
+
+    MOD_INC_USE_COUNT;
+
+    return 0;
+}
+
+static int write_mouse(struct inode *inode,struct file *file,const char *buffer,int count)
+{
+    return -EINVAL;
+}
+
+static int read_mouse(struct inode *inode,struct file *file,char *buffer,int count)
+{
+    unsigned long flags;
+    int dxpos, dypos, i, buttons;
+
+    if (count < 3)
+	return -EINVAL;
+    if ((i = verify_area(VERIFY_WRITE, buffer, count)))
+	return i;
+    if (!mouse_ready)
+	return -EAGAIN;
+
+    save_flags (flags);
+    cli ();
+
+    dxpos = mouse_dxpos;
+    dypos = mouse_dypos;
+    buttons = mouse_buttons ^ 7;
+
+    if (dxpos < -127)
+	dxpos =- 127;
+    if (dxpos > 127)
+	dxpos = 127;
+    if (dypos <- 127)
+	dypos =- 127;
+    if (dypos > 127)
+	dypos = 127;
+
+    mouse_dxpos -= dxpos;
+    mouse_dypos -= dypos;
+    mouse_ready = 0;
+
+    restore_flags (flags);
+
+    put_fs_byte ((char) buttons | 0x80, buffer);
+    put_fs_byte ((char) dxpos, buffer + 1);
+    put_fs_byte ((char) dypos, buffer + 2);
+    for(i = 3; i < count; i++)
+	put_fs_byte (0x00, buffer + i);
+
+    return i;
+}
+
+static int select_mouse(struct inode *inode,struct file *file,int sel_type,select_table *wait)
+{
+    if (sel_type == SEL_IN) {
+	if (mouse_ready)
+	    return 1;
+	select_wait (&mouse_wait,wait);
+    }
+    return 0;
+}
+
+struct file_operations kbd_mouse_fops=
+{
+    NULL,			/* mouse_seek */
+    read_mouse,
+    write_mouse,
+    NULL,			/* mouse_readdir */
+    select_mouse,
+    NULL,			/* mouse_ioctl */
+    NULL,			/* mouse_mmap */
+    open_mouse,
+    close_mouse,
+    NULL,
+    fasync_mouse,
+};
+
+static struct mouse kbd_mouse = {
+    4, "kbdmouse", &kbd_mouse_fops
+};
+
+int arch_mouse_init(void)
+{
+    unsigned long flags;
+
+    save_flags (flags);
+    cli ();
+
+    mouse_buttons=0;
+    mouse_dxpos  =0;
+    mouse_dypos  =0;
+    mouse_present=1;
+    mouse_ready  =0;
+    mouse_active =0;
+    mouse_wait   =NULL;
+
+    restore_flags (flags);
+    mouse_register (&kbd_mouse);
+    return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+    if (MOD_IN_USE)
+	printk ("kcdmouse: in use - remove delayed\n");
+    mouse_deregister (&kbd_mouse);
+}
+#endif
diff -urNwbB linux/arch/arm/drivers/char/keyboard.c linux.arm/arch/arm/drivers/char/keyboard.c
--- linux/arch/arm/drivers/char/keyboard.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/keyboard.c	Sun Mar  3 12:39:34 1996
@@ -0,0 +1,1302 @@
+/*
+ * linux/arch/arm/drivers/block/keyboard.c
+ *
+ * Keyboard driver for Linux using Latin-1.
+ *
+ * Written for linux by Johan Myreen as a translation from
+ * the assembly version by Linus (with diacriticals added)
+ *
+ * Some additional features added by Christoph Niemann (ChN), March 1993
+ *
+ * Loadable keymaps by Risto Kankkunen, May 1993
+ *
+ * Diacriticals redone & other small changes, aeb@cwi.nl, June 93
+ * Added decr/incr_console, dynamic keymaps, unicode support,
+ * dynamic function/string keys, led setting,  Sept 1994
+ * `Sticky' modifier keys, 951006
+ *
+ * Majorly altered for use with arm Russell King (rmk92@ecs.soton.ac.uk).
+ */
+
+#define IRQ_KEYBOARDRX 15
+#define IRQ_KEYBOARDTX 14
+
+#define VERSION 102
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/ptrace.h>
+#include <linux/signal.h>
+#include <linux/timer.h>
+#include <linux/random.h>
+#include <linux/ctype.h>
+
+#include <asm/bitops.h>
+#include <asm/irq.h>
+
+#include "kbd_kern.h"
+#include "diacr.h"
+#include "vt_kern.h"
+
+#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+#define KBD_REPORT_ERR
+#define KBD_REPORT_UNKN
+
+#ifndef KBD_DEFMODE
+#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
+#endif
+
+#ifndef KBD_DEFLEDS
+/*
+ * Starts with NumLock off.
+ */
+#define KBD_DEFLEDS 0
+#endif
+
+#ifndef KBD_DEFLOCK
+#define KBD_DEFLOCK 0
+#endif
+
+#define REPEAT_TIMEOUT HZ*300/1000
+#define REPEAT_RATE    HZ*30/1000
+
+extern void poke_blanked_console (void);
+extern void ctrl_alt_del (void);
+extern void reset_vc (unsigned int new_console);
+extern void change_console (unsigned int console);
+
+/*
+ * global state includes the following, and various static variables
+ * in this module: shift_state, diacr, npadch, dead_key_next.
+ * (last console is now a global variable).
+ */
+
+/* shift state counters.. */
+static unsigned char k_down[NR_SHIFT] = {0, };
+/*
+ * Key down counters
+ */
+#define BITS_PER_SHORT (8*sizeof (unsigned short))
+static unsigned short key_down[256/BITS_PER_SHORT] = {0, };
+
+extern int last_console;
+static int want_console    = -1;
+static int dead_key_next   = 0;
+/*
+ * In order to retrieve the shift_state (for the mouse server), either
+ * the variable must be global, or a new procedure must be created to
+ * return the value. I chose the former way.
+ */
+/*static*/ int shift_state   = 0;
+static int npadch            = -1;		/* -1 or number assembled on pad */
+static unsigned char diacr   = 0;
+static char rep              = 0;		/* Flag telling character repeat */
+static int kbd_repeatkey     = -1;
+static int kbd_repeattimeout = REPEAT_TIMEOUT;
+static int kbd_repeatrate    = REPEAT_RATE;
+
+struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+static struct tty_struct **ttytab;
+static struct kbd_struct * kbd = kbd_table;
+static struct tty_struct * tty = NULL;
+
+extern void compute_shiftstate (void);
+
+typedef void (*k_hand)(unsigned char value, char up_flag);
+typedef void (k_handfn)(unsigned char value, char up_flag);
+
+static k_handfn
+	do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
+	do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
+
+static k_hand key_handler[16] = {
+	do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
+	do_meta, do_ascii, do_lock, do_lowercase, do_slock,
+	do_ignore, do_ignore, do_ignore
+};
+
+typedef void (*void_fnp)(void);
+typedef void (void_fn)(void);
+
+static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
+	num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
+	SAK, decr_console, incr_console, spawn_console, show_stack, bare_num;
+
+static void_fnp spec_fn_table[] = {
+	do_null,	enter,		show_ptregs,	show_mem,
+	show_state,	send_intr,	lastcons,	caps_toggle,
+	num,		hold,		scroll_forw,	scroll_back,
+	boot_it,	caps_on,	compose,	SAK,
+	decr_console,	incr_console,	show_stack,     bare_num
+};
+
+/* maximum values each key_handler can handle */
+const int max_vals[] = {
+	255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
+	NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
+	255, NR_ASCII - 1, NR_LOCK - 1, 255,
+	NR_LOCK - 1
+};
+
+const int NR_TYPES = SIZE(max_vals);
+
+static void put_queue(int);
+static unsigned char handle_diacr(unsigned char);
+
+/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
+static struct pt_regs * pt_regs;
+
+/*
+ * Many other routines do put_queue, but I think either
+ * they produce ASCII, or they produce some user-assigned
+ * string, and in both cases we might assume that it is
+ * in utf-8 already.
+ */
+void to_utf8(ushort c) {
+    if (c < 0x80)
+	put_queue(c);			/*  0*******  */
+    else if (c < 0x800) {
+	put_queue(0xc0 | (c >> 6)); 	/*  110***** 10******  */
+	put_queue(0x80 | (c & 0x3f));
+    } else {
+	put_queue(0xe0 | (c >> 12)); 	/*  1110**** 10****** 10******  */
+	put_queue(0x80 | ((c >> 6) & 0x3f));
+	put_queue(0x80 | (c & 0x3f));
+    }
+     /* uft-8 is defined for words of up to 36 bits,
+       but we need only 16 bits here */
+}
+
+/*
+ * ARM mouse specifics stuff.
+ *
+ * Since the mouse movements and buttons are part of the
+ * keyboard protocol, we have to handle this part in this
+ * driver.  The mouse specific parts are in arcmouse.c.
+ */
+#ifdef CONFIG_KBDMOUSE
+extern struct wait_queue *mouse_wait;
+extern char		mouse_buttons;
+extern int		mouse_dxpos;
+extern int		mouse_dypos;
+extern char		mouse_ready;
+#endif
+/*
+ * Protocol codes to send the keyboard.
+ */
+/* reset keyboard */
+#define HRST 0xff
+/* reset response */
+#define RAK1 0xfe
+/* reset response */
+#define RAK2 0xfd
+/* Ack for first keyboard pair */
+#define BACK 0x3f
+/* Last data byte ack (key scanning + mouse movement scanning) */
+#define SMAK 0x33
+/* Last data byte ack (mouse movement scanning) */
+#define MACK 0x32
+/* Last data byte ack (key scanning) */
+#define SACK 0x31
+/* Request mouse data */
+#define RQMP 0x22
+/* nothing */
+#define PRST 0x21
+/* Request ID */
+#define RQID 0x20
+
+#define UP_FLAG 0x0100
+
+static unsigned char	kbd_sendvala[4];
+static unsigned char	kbd_sendptri;
+static unsigned char	kbd_sendptro;
+static unsigned char    ledstate = 0xff;
+static unsigned char getleds(void);
+extern unsigned char	*ioc;
+
+/*
+ * This array converts the scancode that we get from the keyboard to the
+ * real rows/columns on the A5000 keyboard.  This might be keyboard specific...
+ *
+ * It is these values that we use to maintain the key down array.  That way, we
+ * should pick up on the ghost key presses (which is what happens when you press
+ * three keys, and the keyboard thinks you have pressed four!)
+ *
+ * Row 8 (0x80+c) is actually a column with one key per row.  It is isolated from
+ * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc).
+ *
+ * Illegal scancodes are denoted by an 0xff (in other words, we don't know about
+ * them, and can't process them for ghosts).  This does however, cause problems with
+ * autorepeat processing...
+ */
+static unsigned char scancode_2_colrow[256] = {
+  0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60,
+  0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79,
+  0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a,
+  0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34,
+  0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12,
+  0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03,
+  0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff,
+};
+
+static unsigned short ghost_down[128/BITS_PER_SHORT];
+
+/*
+ * As yet, we don't support setting and getting the
+ * key codes.  I'll have to find out where this is used.
+ */
+int setkeycode(unsigned int scancode, unsigned int keycode)
+{
+#if 0
+	if (scancode < SC_LIM || scancode > 255 || keycode > 127)
+		return -EINVAL;
+	if (scancode < 128)
+		high_keys[scancode - SC_LIM] = keycode;
+	else
+		e0_keys[scancode - 128] = keycode;
+	return 0;
+#else
+	return -EINVAL;
+#endif
+}
+
+int getkeycode(unsigned int scancode)
+{
+#if 0
+	return
+		(scancode < SC_LIM || scancode > 255) ? -EINVAL :
+		(scancode < 128) ? high_keys[scancode - SC_LIM] :
+		e0_keys[scancode - 128];
+#else
+	return -EINVAL;
+#endif
+}
+
+
+/* ----------------------------------------------------------------------------------------- */
+/*
+ * This is the keyboard specific part.
+ */
+static void key_callback(unsigned long nr)
+{
+  rep = 1;
+  mark_bh (KEYBOARD_BH);
+}
+
+static struct timer_list key_timer =
+{
+  NULL, NULL, 0, 0, key_callback
+};
+
+static void kbd_key(unsigned int raw_keycode, int repeat_timeout)
+{
+    unsigned int up_flag;
+    unsigned int keycode;
+    unsigned char real_keycode;
+
+    up_flag = raw_keycode & UP_FLAG;
+    keycode = raw_keycode & 255;
+
+    /*
+     * Separate out the mouse stuff first
+     */
+    if (keycode >= 0x70 && keycode <= 0x72) {
+#ifdef CONFIG_KBDMOUSE
+	switch (keycode) {
+	case 0x70: /* Left mouse button */
+	    if (up_flag)
+		mouse_buttons &= ~4;
+	    else
+		mouse_buttons |= 4;
+	    break;
+
+	case 0x71: /* Middle mouse button */
+	    if (up_flag)
+		mouse_buttons &= ~2;
+	    else
+		mouse_buttons |= 2;
+	    break;
+
+	case 0x72:/* Right mouse button */
+	    if (up_flag)
+		mouse_buttons &= ~1;
+	    else
+		mouse_buttons |= 1;
+	    break;
+	default:
+#ifdef KBD_REPORT_UNKN
+	    printk ("kbd: unknown scancode 0x%04x\n", raw_keycode);
+#endif
+	    return;
+	}
+	add_mouse_randomness (mouse_buttons << 16);
+	mouse_ready = 1;
+	wake_up_interruptible (&mouse_wait);
+#endif
+	mark_bh (KEYBOARD_BH);
+	return;
+    }
+
+    if (keycode >= 0x70) {
+#ifdef KBD_REPORT_UNKN
+	printk ("kbd: unknown scancode 0x%04x\n", raw_keycode);
+#endif
+	return;
+    }
+
+    real_keycode = scancode_2_colrow[keycode];
+
+    /*
+     * We have to work out if we accept this key press as a real key, or
+     * if it is a ghost.  IE. If you press three keys, the keyboard will think
+     * that you've pressed a fouth: (@ = key down, # = ghost)
+     *
+     *   0 1 2 3 4 5 6 7
+     *   | | | | | | | |
+     * 0-+-+-+-+-+-+-+-+-
+     *   | | | | | | | |
+     * 1-+-@-+-+-+-@-+-+-
+     *   | | | | | | | |
+     * 2-+-+-+-+-+-+-+-+-
+     *   | | | | | | | |
+     * 3-+-@-+-+-+-#-+-+-
+     *   | | | | | | | |
+     *
+     * This is what happens when you have a matrix keyboard...
+     */
+
+    if ((real_keycode & 0x80) == 0) {
+	int rr, kc = (real_keycode >> 4) & 7;
+	int cc;
+	unsigned short res, kdownkc;
+
+	kdownkc = ghost_down[kc] | (1 << (real_keycode & 15));
+
+	for (rr = 0; rr < 128/BITS_PER_SHORT; rr++)
+	    if (rr != kc && (res = ghost_down[rr] & kdownkc)) {
+	    	/*
+		 * we have found a second row with at least one key pressed in the
+	    	 * same column.
+	    	 */
+	    	for (cc = 0; res; res >>= 1)
+		    cc += (res & 1);
+
+		if (cc > 1)
+		    return; /* ignore it */
+	    }
+	if (up_flag)
+	    clear_bit (real_keycode, ghost_down);
+	else
+	    set_bit (real_keycode, ghost_down);
+
+    }
+
+    if (kbd_repeatkey == (raw_keycode & ~UP_FLAG) || !up_flag)
+	kbd_repeatkey = -1;
+
+    add_keyboard_randomness (raw_keycode);
+
+    /*
+     * We have to disable interrupts here to prevent the timer from
+     * being put on the list twice or a key being released during
+     * the processing of autorepeat
+     */
+    del_timer (&key_timer);
+
+    tty = ttytab[fg_console];
+    kbd = kbd_table + fg_console;
+
+    if (up_flag) {
+    	rep = 0;
+	clear_bit (keycode, key_down);
+	/* don't report an error if key is already up - happens when a ghost key is released */
+    } else
+	rep = set_bit (keycode, key_down);
+
+    if (kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) {
+	put_queue (keycode | (up_flag ? 0x80 : 0));
+	return;
+    }
+
+    /*
+     * We have to do our own auto-repeat processing...
+     */
+    if (!up_flag && repeat_timeout) {
+    	kbd_repeatkey = raw_keycode;
+	if (vc_kbd_mode(kbd, VC_REPEAT)) {
+	    key_timer.expires = jiffies + repeat_timeout;
+	    add_timer(&key_timer);
+	}
+    }
+
+    /*
+     * Repeat a key only if the input buffers are empty or the
+     * characters get echoed locally.  This makes key repeat
+     * usable with slow applications and under heavy loads.
+     */
+    if (!rep || (vc_kbd_mode (kbd, VC_REPEAT) && tty &&
+               (L_ECHO(tty) || (tty->driver.chars_in_buffer (tty) == 0)))) {
+	u_short keysym;
+	u_char  type;
+
+	/* the XOR below used to be an OR */
+	int     shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
+	u_short *key_map = key_maps[shift_final];
+
+	if (key_map != NULL) {
+	    keysym = key_map[keycode];
+	    type   = KTYP(keysym);
+
+	    if (type >= 0xf0) {
+		type -= 0xf0;
+
+		if (type == KT_LETTER) {
+		    type = KT_LATIN;
+		    if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
+			key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
+			if (key_map)
+			    keysym = key_map[keycode];
+		    }
+		}
+		(*key_handler[type])(keysym & 0xff, up_flag ? 0x80 : 0);
+		if (type != KT_SLOCK)
+		    kbd->slockstate = 0;
+	    } else {
+		if (!up_flag)
+		    to_utf8(keysym);
+	    }
+	} else {
+	    /*
+	     * maybe beep?
+	     * we have at least to update shift_state
+	     * how? two almost equivalent choices follow
+	     */
+#if 1
+	    compute_shiftstate ();
+#else
+	    keysym = U(plain_map[keycode]);
+	    type   = KTYP(keysym);
+	    if (type == KT_SHIFT)
+		(*key_handler[type]) (keysym & 0xff, up_flag ? 0x80 : 0);
+#endif
+	}
+    }
+}
+
+static void kbd_sendval(unsigned char val)
+{
+    kbd_sendvala[kbd_sendptri] = val;
+    kbd_sendptri = (kbd_sendptri + 1) & 3;
+    disable_irq (15);
+    enable_irq (14);
+}
+
+static void kbd_reset(void)
+{
+    int i;
+
+    for (i = 0; i < NR_SHIFT; i++)
+	k_down[i] = 0;
+
+    for (i = 0; i < 256/BITS_PER_SHORT; i++)
+	key_down[i] = 0;
+
+    for (i = 0; i < 128/BITS_PER_SHORT; i++)
+	ghost_down[i] = 0;
+
+    shift_state = 0;
+}
+
+/*
+ * Keyboard states:
+ *  0 initial reset condition, sent HRST, wait for HRST
+ *  1 Sent RAK1, wait for RAK1
+ *  2 Sent RAK2, wait for RAK2
+ *  3 Sent SMAK, wait for *
+ *  4 Wait for second keyboard nibble for key pressed
+ *  5 Wait for second keyboard nibble for key released
+ *  6 Wait for second part of mouse data
+ */
+
+static void kbd_rx(int irq, struct pt_regs *regs)
+{
+    int keyval;
+    static char kbd_mousedx=0;
+    char kbd_mousedy;
+    static unsigned char kbd_state=0;
+    static unsigned char kbd_keyhigh=0;
+
+    pt_regs = regs;
+
+    keyval=ioc[4];
+
+    switch(kbd_state) {
+    case 0:/* initial reset condition */
+	if (keyval == HRST) {
+	    kbd_reset ();
+	    kbd_sendval (RAK1);
+	    kbd_state = 1;
+	} else
+	    goto kbd_wontreset;
+	break;
+
+    case 1:/* Sent RAK1 */
+	if (keyval == RAK1) {
+	    kbd_sendval (RAK2);
+	    kbd_state = 2;
+	} else
+	    goto kbd_wontreset;
+	break;
+
+    case 2:/* Sent RAK2 */
+	if (keyval == RAK2) {
+	    kbd_sendval (SMAK);
+	    kbd_state = 3;
+	    ledstate  = 0xff;
+	    mark_bh (KEYBOARD_BH);
+	} else
+	    goto kbd_wontreset;
+	break;
+
+    case 3:/* Send SMAK, ready for any reply */
+	if (keyval == HRST) {
+	    kbd_sendval (HRST);
+	    kbd_state = 0;
+	} else
+	if (keyval & 0x80) {
+	    switch (keyval & 0xf0) {
+	    case 0xc0:
+		kbd_keyhigh = keyval;
+		kbd_state   = 4;
+		kbd_sendval (BACK);
+		break;
+
+	    case 0xd0:
+		kbd_keyhigh = keyval;
+		kbd_state   = 5;
+		kbd_sendval (BACK);
+		break;
+
+	    default:
+		kbd_state = 0;
+		kbd_sendval (HRST);
+	    }
+	} else {
+	    kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval;
+	    kbd_state   = 6;
+	    kbd_sendval (BACK);
+	}
+	break;
+
+    case 4:
+	if ((keyval & 0xf0) != 0xc0)
+	    goto kbd_error;
+	else {
+	    kbd_state = 3;
+	    kbd_sendval (SMAK);
+	    if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) {
+		kbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), kbd_repeattimeout);
+		mark_bh (KEYBOARD_BH);
+	    }
+	}
+	break;
+
+    case 5:
+	if ((keyval & 0xf0) != 0xd0)
+	    goto kbd_error;
+	else {
+	    kbd_state = 3;
+	    kbd_sendval (SMAK);
+	    if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) {
+		kbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0) | UP_FLAG, 0);
+		mark_bh (KEYBOARD_BH);
+	    }
+	}
+	break;
+
+    case 6:
+	if (keyval & 0x80)
+	    goto kbd_error;
+	else {
+	    kbd_state = 3;
+	    kbd_sendval (SMAK);
+	    kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval);
+#ifdef CONFIG_KBDMOUSE
+	    mouse_dypos += (int)kbd_mousedy;
+	    mouse_dxpos += (int)kbd_mousedx;
+	    add_mouse_randomness ((mouse_buttons << 16) + (kbd_mousedy << 8) + kbd_mousedx);
+	    mouse_ready = 1;
+	    wake_up_interruptible (&mouse_wait);
+#endif
+	    mark_bh (KEYBOARD_BH);
+	}
+	break;
+    }
+    return;
+kbd_wontreset:
+#ifdef KBD_REPORT_ERR
+    printk ("kbd: keyboard won't reset\n");
+#endif
+    kbd_sendval (HRST);
+    kbd_state = 0;
+    return;
+kbd_error:
+#ifdef KBD_REPORT_ERR
+    printk ("kbd: keyboard out of sync - resetting\n");
+#endif
+    kbd_sendval (HRST);
+    kbd_state = 0;
+    return;
+}
+
+static void kbd_tx(int irq, struct pt_regs *regs)
+{
+  if(kbd_sendptri!=kbd_sendptro)
+  {
+    ioc[0x04]=kbd_sendvala[kbd_sendptro];
+    kbd_sendptro=(kbd_sendptro+1)&3;
+  }
+  if(kbd_sendptri==kbd_sendptro)
+  {
+    disable_irq(14);
+    enable_irq(15);
+  }
+}
+
+/* ----------------------------------------------------------------------------------------- */
+/*
+ * Here starts a copy of the PC keyboard code from linux/drivers/char/keyboard.c
+ */
+
+static void put_queue (int ch)
+{
+    wake_up (&keypress_wait);
+    if (tty) {
+	tty_insert_flip_char (tty, ch, 0);
+	tty_schedule_flip (tty);
+    }
+}
+
+static void puts_queue(char *cp)
+{
+    wake_up (&keypress_wait);
+    if (!tty)
+	return;
+
+    while (*cp)
+	tty_insert_flip_char (tty, *cp++, 0);
+    tty_schedule_flip (tty);
+}
+
+static void applkey(int key, char mode)
+{
+    static char buf[] = { 0x1b, 'O', 0x00, 0x00};
+
+    buf[1] = (mode ? 'O' : '[');
+    buf[2] = key;
+    puts_queue(buf);
+}
+
+static void enter(void)
+{
+    put_queue (13);
+    if (vc_kbd_mode(kbd, VC_CRLF))
+	put_queue (10);
+}
+
+static void caps_toggle(void)
+{
+    if(rep)
+	return;
+    chg_vc_kbd_led(kbd, VC_CAPSLOCK);
+}
+
+static void caps_on (void)
+{
+    if (rep)
+	return;
+    set_vc_kbd_led (kbd, VC_CAPSLOCK);
+}
+
+static void show_ptregs (void)
+{
+    extern void register_dump (struct pt_regs *);
+    if (pt_regs)
+	register_dump (pt_regs);
+}
+
+static void show_stack (void)
+{
+    unsigned long *p;
+    unsigned long *q = (unsigned long *)((int)current->kernel_stack_page + 4096);
+    int i;
+    __asm__("mov %0, sp\n\t": "=r" (p));
+
+    for(i=0; p < q; p++, i++) {
+	if(i && !(i & 7))
+	    printk("\n");
+	printk("%08lX ", *p);
+    }
+}
+
+static void hold (void)
+{
+    if(rep || !tty)
+	return;
+
+    /*
+     * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
+     * these routines are also activated by ^S/^Q.
+     * (And SCROLLOCK can also be set by the KDSKBLED.)
+     */
+    if (tty->stopped)
+	start_tty (tty);
+    else
+	stop_tty (tty);
+}
+
+static void num (void)
+{
+    if(vc_kbd_mode(kbd,VC_APPLIC))
+	applkey('P', 1);
+    else
+    	bare_num ();
+}
+
+/*
+ * Bind this to Shift-NumLock if you work in application keypad mode
+ * but want to be able to change the NumLock flag.
+ * Bind this to NumLock if you prefer that the NumLock key always
+ * changes the NumLock flag.
+ */
+static void bare_num (void)
+{
+    if (!rep)
+	chg_vc_kbd_led (kbd, VC_NUMLOCK);
+}
+
+static void lastcons (void)
+{
+    want_console = last_console;
+}
+
+static void decr_console (void)
+{
+    int i;
+
+    for (i = fg_console - 1; i != fg_console; i--) {
+	if (i == -1)
+	    i = MAX_NR_CONSOLES - 1;
+	if (vc_cons_allocated (i))
+	    break;
+    }
+    want_console = i;
+}
+
+static void incr_console (void)
+{
+    int i;
+
+    for (i = fg_console + 1; i != fg_console; i++) {
+	if (i == MAX_NR_CONSOLES)
+	    i = 0;
+	if (vc_cons_allocated (i))
+	    break;
+    }
+    want_console = i;
+}
+
+static void send_intr (void)
+{
+    if (!tty)
+	return;
+    tty_insert_flip_char (tty, 0, TTY_BREAK);
+    tty_schedule_flip (tty);
+}
+
+static void scroll_forw (void)
+{
+}
+
+static void scroll_back (void)
+{
+}
+
+static void boot_it (void)
+{
+    ctrl_alt_del();
+}
+
+static void compose (void)
+{
+    dead_key_next = 1;
+}
+
+int spawnpid, spawnsig;
+
+static void spawn_console (void)
+{
+    if (spawnpid)
+	if (kill_proc (spawnpid, spawnsig, 1))
+	    spawnpid = 0;
+}
+
+static void SAK (void)
+{
+    do_SAK(tty);
+#if 0
+    /*
+     * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
+     * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
+     * handling.
+     *
+     * We should do this some day --- the whole point of a secure
+     * attention key is that it should be guaranteed to always
+     * work.
+     */
+    reset_vc (fg_console);
+    do_unblank_screen ();	/* not in interrupt routine? */
+#endif
+}
+
+static void do_ignore (unsigned char value, char up_flag)
+{
+}
+
+static void do_null (void)
+{
+    compute_shiftstate ();
+}
+
+static void do_spec (unsigned char value,char up_flag)
+{
+    if (up_flag)
+	return;
+    if (value >= SIZE(spec_fn_table))
+	return;
+    if (!spec_fn_table[value])
+	return;
+    spec_fn_table[value] ();
+}
+
+static void do_lowercase (unsigned char value, char up_flag)
+{
+    printk("keyboard.c: do_lowercase was called - impossible\n");
+}
+
+static void do_self(unsigned char value, char up_flag)
+{
+    if (up_flag)
+	return;		/* no action, if this is a key release */
+
+    if (diacr)
+	value = handle_diacr (value);
+
+    if (dead_key_next) {
+	dead_key_next = 0;
+	diacr = value;
+	return;
+    }
+    put_queue (value);
+}
+
+#define A_GRAVE '`'
+#define A_ACUTE '\''
+#define A_CFLEX '^'
+#define A_TILDE '~'
+#define A_DIAER '"'
+static unsigned char ret_diacr[] = {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
+
+static void do_dead(unsigned char value, char up_flag)
+{
+    if (up_flag)
+	return;
+
+    value = ret_diacr[value];
+    if (diacr == value) { /* pressed twice */
+	diacr = 0;
+	put_queue (value);
+	return;
+    }
+    diacr = value;
+}
+
+static unsigned char handle_diacr(unsigned char ch)
+{
+    int d = diacr;
+    int i;
+
+    diacr = 0;
+    if (ch == ' ')
+	return d;
+
+    for (i = 0; i < accent_table_size; i++) {
+	if (accent_table[i].diacr == d && accent_table[i].base == ch)
+	    return accent_table[i].result;
+    }
+
+    put_queue (d);
+    return ch;
+}
+
+static void do_cons(unsigned char value, char up_flag)
+{
+    if (up_flag)
+	return;
+    want_console = value;
+}
+
+static void do_fn(unsigned char value, char up_flag)
+{
+    if (up_flag)
+	return;
+
+    if (value < SIZE(func_table)) {
+	if (func_table[value])
+	    puts_queue (func_table[value]);
+    } else
+	printk ("do_fn called with value=%d\n",value);
+}
+
+static void do_pad(unsigned char value, char up_flag)
+{
+    static char *pad_chars = "0123456789+-*/\015,.?#";
+    static char *app_map = "pqrstuvwxylSRQMnn?#";
+
+    if (up_flag)
+	return;
+
+    if (vc_kbd_mode (kbd, VC_APPLIC) && !k_down[KG_SHIFT]) {
+	applkey (app_map[value], 1);
+	return;
+    }
+
+    if (!vc_kbd_led (kbd,VC_NUMLOCK))
+	switch (value) {
+	    case 15:
+	    case 16: do_fn (22, 0); return;
+	    case 7:  do_fn (20, 0); return;
+	    case 8:  do_cur( 3, 0); return;
+	    case 9:  do_fn (24, 0); return;
+	    case 4:  do_cur( 1, 0); return;
+	    case 6:  do_cur( 2, 0); return;
+	    case 1:  do_fn (23, 0); return;
+	    case 2:  do_cur( 0, 0); return;
+	    case 3:  do_fn (25, 0); return;
+	    case 5:  applkey('G',vc_kbd_mode(kbd, VC_APPLIC)); return;
+    }
+
+    put_queue (pad_chars[value]);
+    if (value == 14 && vc_kbd_mode (kbd, VC_CRLF))
+	put_queue (10);
+}
+
+static void do_cur(unsigned char value, char up_flag)
+{
+    static char *cur_chars = "BDCA";
+
+    if (up_flag)
+	return;
+
+    applkey (cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
+}
+
+static void do_shift(unsigned char value, char up_flag)
+{
+    int old_state = shift_state;
+
+    if (rep)
+	return;
+
+    /* Mimic typewriter: a CapsShift key acts like Shift but undoes CapsLock */
+    if (value == KVAL(K_CAPSSHIFT)) {
+	value = KVAL(K_SHIFT);
+	if (!up_flag)
+	    clr_vc_kbd_led (kbd, VC_CAPSLOCK);
+    }
+
+    if(up_flag) {
+	/* handle the case that two shift or control
+	   keys are depressed simultaneously */
+	if(k_down[value])
+	    k_down[value]--;
+    } else
+	k_down[value]++;
+
+    if(k_down[value])
+	shift_state |= (1 << value);
+    else
+	shift_state &= ~ (1 << value);
+
+    /* kludge */
+    if(up_flag && shift_state != old_state && npadch != -1) {
+	if(kbd->kbdmode == VC_UNICODE)
+	    to_utf8(npadch & 0xffff);
+	else
+	    put_queue(npadch & 0xff);
+	npadch = -1;
+    }
+}
+
+/*
+ * Called after returning from RAW mode or when changing consoles -
+ * recompute k_down[] and shift_state from key_down[]
+ * Maybe called when keymap is undefined so that shift key release is seen
+ */
+void compute_shiftstate(void)
+{
+    int i, j, k, sym, val;
+
+    shift_state = 0;
+    for (i = 0; i < SIZE(k_down); i++)
+	k_down[i] = 0;
+
+    for (i = 0; i < SIZE(key_down); i++)
+	if (key_down[i]) {			/* skip this short word if zero */
+	    k = i * BITS_PER_SHORT;
+	    for (j = 0; j < BITS_PER_SHORT; j++, k++)
+		if (test_bit (k, key_down)) {
+		    sym = U(plain_map[k]);
+		    if (KTYP(sym) == KT_SHIFT) {
+			val = KVAL (sym);
+			if (val == KVAL(K_CAPSSHIFT))
+			    val = KVAL(K_SHIFT);
+			k_down[val] ++;
+			shift_state |= (1 << val);
+		    }
+		}
+	}
+}
+
+static void do_meta(unsigned char value, char up_flag)
+{
+    if(up_flag)
+	return;
+
+    if(vc_kbd_mode(kbd, VC_META)) {
+	put_queue ('\033');
+	put_queue (value);
+    } else
+	put_queue (value | 0x80);
+}
+
+static void do_ascii(unsigned char value, char up_flag)
+{
+    int base;
+
+    if(up_flag)
+	return;
+
+    if(value < 10)
+	base = 10;
+    else {
+	value -= 10;
+	base = 16;
+    }
+
+    if(npadch == -1)
+	npadch = value;
+    else
+	npadch = npadch * base + value;
+}
+
+static void do_lock (unsigned char value, char up_flag)
+{
+    if (up_flag || rep)
+	return;
+    chg_vc_kbd_lock (kbd, value);
+}
+
+static void do_slock (unsigned char value, char up_flag)
+{
+    if (up_flag || rep)
+	return;
+    chg_vc_kbd_slock (kbd, value);
+}
+
+/* --------------------------------------------------------------------------------------- *
+ * Led driver										   *
+ * --------------------------------------------------------------------------------------- */
+
+/*
+ * The leds display either
+ * (i)   The status of NumLock, CapsLock and ScrollLock.
+ * (ii)  Whatever pattern of lights people wait to show using KDSETLED.
+ * (iii) Specified bits of specified words in kernel memory.
+ */
+
+static unsigned char ledioctl;
+
+unsigned char getledstate(void)
+{
+    return ledstate;
+}
+
+void setledstate(struct kbd_struct *kbd, unsigned int led)
+{
+    if(!(led & ~7)) {
+	ledioctl = led;
+	kbd->ledmode = LED_SHOW_IOCTL;
+    } else
+	kbd->ledmode = LED_SHOW_FLAGS;
+    set_leds();
+}
+
+static struct ledptr
+{
+    unsigned int *addr;
+    unsigned int mask;
+    unsigned char valid:1;
+} ledptrs[3];
+
+void register_leds(int console,unsigned int led,unsigned int *addr,unsigned int mask)
+{
+    struct kbd_struct *kbd = kbd_table + console;
+
+    if(led < 3) {
+	ledptrs[led].addr = addr;
+	ledptrs[led].mask = mask;
+	ledptrs[led].valid= 1;
+	kbd->ledmode = LED_SHOW_MEM;
+    } else
+	kbd->ledmode = LED_SHOW_FLAGS;
+}
+
+static inline unsigned char getleds(void)
+{
+    struct kbd_struct *kbd = kbd_table + fg_console;
+    unsigned char leds;
+
+    if(kbd->ledmode == LED_SHOW_IOCTL)
+	return ledioctl;
+
+    leds = kbd->ledflagstate;
+    if (kbd->ledmode == LED_SHOW_MEM) {
+	if (ledptrs[0].valid) {
+	    if (*ledptrs[0].addr & ledptrs[0].mask)
+		leds |= 1;
+	    else
+		leds &= ~1;
+        }
+	if (ledptrs[1].valid) {
+	    if (*ledptrs[1].addr & ledptrs[1].mask)
+		leds |= 2;
+	    else
+		leds &= ~2;
+        }
+	if (ledptrs[2].valid) {
+	    if(*ledptrs[2].addr & ledptrs[2].mask)
+		leds |= 4;
+	    else
+		leds &= ~4;
+        }
+    }
+    return leds;
+}
+
+/*
+ * This routine is the bottom half of the keyboard interrupt
+ * routine, and runs with all interrupts enabled.  It does
+ * console changing, led setting, and copy_to_cooked, which can
+ * take a reasonably long time.
+ *
+ * Aside from timing (which isn't really that important for
+ * keyboard interrupts as they happen often), using the software
+ * interrupt routines for this thing allows us to easily mask
+ * this when we don't want any of the above to happen.  Not yet
+ * used, but this allows for easy and efficient race-condition
+ * prevention later on.
+ */
+static void kbd_bh(void *unused)
+{
+    unsigned char leds;
+
+    tty = ttytab[fg_console];
+    kbd = kbd_table + fg_console;
+
+    if (rep && kbd_repeatkey != -1) {
+    	/*
+    	 * This prevents the kbd_key routine from being called
+    	 * twice, once by this BH, and once by the interrupt
+    	 * routine.  Maybe we should put the key press in a
+    	 * buffer or variable, and then call the BH...
+    	 */
+    	disable_irq (IRQ_KEYBOARDRX);
+	kbd_key (kbd_repeatkey, kbd_repeatrate);
+	enable_irq (IRQ_KEYBOARDRX);
+        rep = 0;
+    }
+
+    if(want_console >= 0) {
+	if(want_console != fg_console) {
+	    change_console(want_console);
+	    /* we only changed when the console had already
+	     * been allocated - a new console is not created
+	     * in an interrupt routine.
+	     */
+	}
+	want_console = -1;
+    }
+
+    leds = getleds ();
+    if (leds != ledstate) {
+	unsigned long flags;
+	ledstate = leds;
+	leds = ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) |
+	       ((leds & (1<<VC_CAPSLOCK))?1:0);
+
+	save_flags(flags);
+	cli();
+	intr_count -= 1;
+
+	kbd_sendval(leds);
+
+	intr_count += 1;
+	restore_flags(flags);
+    }
+    poke_blanked_console();
+}
+
+int kbd_init (void)
+{
+    int i;
+    struct kbd_struct kbd0;
+    extern struct tty_driver console_driver;
+    unsigned long flags;
+
+    kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS;
+    kbd0.ledmode      = LED_SHOW_FLAGS;
+    kbd0.lockstate    = KBD_DEFLOCK;
+    kbd0.slockstate   = 0;
+    kbd0.modeflags    = KBD_DEFMODE;
+    kbd0.kbdmode      = VC_XLATE;
+
+    for (i = 0 ; i < MAX_NR_CONSOLES ; i++)
+	kbd_table[i] = kbd0;
+
+    ttytab = console_driver.table;
+
+    bh_base[KEYBOARD_BH].routine = kbd_bh;
+
+    save_flags (flags);
+    cli ();
+    if (request_irq (IRQ_KEYBOARDRX, kbd_rx, 0, "keyboard")!=0)
+	panic("Could not allocate keyboard receive IRQ!");
+    if (request_irq (IRQ_KEYBOARDTX, kbd_tx, 0, "Keyboard")!=0)
+	panic("Could not allocate keyboard transmit IRQ!");
+    disable_irq (IRQ_KEYBOARDTX);
+    restore_flags(flags);
+
+    kbd_sendval(HRST);
+    mark_bh (KEYBOARD_BH);
+    enable_bh (KEYBOARD_BH);
+
+    printk("Keyboard driver installed (%s) v%d.%02d.\n", "English", VERSION/100, VERSION%100);
+    return 0;
+}
+
diff -urNwbB linux/arch/arm/drivers/char/mem.c linux.arm/arch/arm/drivers/char/mem.c
--- linux/arch/arm/drivers/char/mem.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/mem.c	Sun Mar  3 12:45:19 1996
@@ -0,0 +1,383 @@
+/*
+ *  linux/arch/arm/drivers/char/mem.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Modifications for ARM Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/tty.h>
+#include <linux/mouse.h>
+#include <linux/tpqic02.h>
+#include <linux/malloc.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_SOUND
+void soundcard_init(void);
+#endif
+
+static int read_ram(struct inode * inode, struct file * file, char * buf, int count)
+{
+	return -EIO;
+}
+
+static int write_ram(struct inode * inode, struct file * file, const char * buf, int count)
+{
+	return -EIO;
+}
+
+static int read_mem(struct inode * inode, struct file * file, char * buf, int count)
+{
+	unsigned long p = file->f_pos;
+
+	p += PAGE_OFFSET;
+	if (count < 0)
+		return -EINVAL;
+	if (MAP_NR(p) >= MAP_NR(high_memory))
+		return 0;
+	if (count > high_memory - p)
+		count = high_memory - p;
+	memcpy_tofs(buf,(void *) p,count);
+	file->f_pos += count;
+	return count;
+}
+
+static int write_mem(struct inode * inode, struct file * file, const char * buf, int count)
+{
+	unsigned long p = file->f_pos;
+
+	p += PAGE_OFFSET;
+	if (count < 0)
+		return -EINVAL;
+	if (MAP_NR(p) >= MAP_NR(high_memory))
+		return 0;
+	if (count > high_memory - p)
+		count = high_memory - p;
+	memcpy_fromfs((void *) p,buf,count);
+	file->f_pos += count;
+	return count;
+}
+
+static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+	if (vma->vm_offset & ~PAGE_MASK)
+		return -ENXIO;
+
+	if (remap_page_range(vma->vm_start, vma->vm_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+		return -EAGAIN;
+	vma->vm_inode = inode;
+	inode->i_count++;
+	return 0;
+}
+
+static int read_kmem(struct inode *inode, struct file *file, char *buf, int count)
+{
+	int read1, read2;
+
+	read1 = read_mem(inode, file, buf, count);
+	if (read1 < 0)
+		return read1;
+	read2 = vread(buf + read1, (char *) ((unsigned long) file->f_pos), count - read1);
+	if (read2 < 0)
+		return read2;
+	file->f_pos += read2;
+	return read1 + read2;
+}
+
+static int read_port(struct inode * inode, struct file * file,char * buf, int count)
+{
+	unsigned int i = file->f_pos;
+	char * tmp = buf;
+
+	while (count-- > 0 && i < 65536) {
+		put_user(inb(i),tmp);
+		i++;
+		tmp++;
+	}
+	file->f_pos = i;
+	return tmp-buf;
+}
+
+static int write_port(struct inode * inode, struct file * file, const char * buf, int count)
+{
+	unsigned int i = file->f_pos;
+	const char * tmp = buf;
+
+	while (count-- > 0 && i < 65536) {
+		outb(get_user(tmp),i);
+		i++;
+		tmp++;
+	}
+	file->f_pos = i;
+	return tmp-buf;
+}
+
+static int read_null(struct inode * node, struct file * file, char * buf, int count)
+{
+	return 0;
+}
+
+static int write_null(struct inode * inode, struct file * file, const char * buf, int count)
+{
+	return count;
+}
+
+static int read_zero(struct inode * node, struct file * file, char * buf, int count)
+{
+	int left;
+
+	for (left = count; left > 0; left--) {
+		put_user(0,buf);
+		buf++;
+	}
+	return count;
+}
+
+static int mmap_zero(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+	if (vma->vm_flags & VM_SHARED)
+		return -EINVAL;
+	if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+		return -EAGAIN;
+	return 0;
+}
+
+static int read_full(struct inode * node, struct file * file, char * buf,int count)
+{
+	return count;
+}
+
+static int write_full(struct inode * inode, struct file * file, const char * buf, int count)
+{
+	return -ENOSPC;
+}
+
+/*
+ * Special lseek() function for /dev/null and /dev/zero.  Most notably, you can fopen()
+ * both devices with "a" now.  This was previously impossible.  SRB.
+ */
+
+static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
+{
+	return file->f_pos=0;
+}
+/*
+ * The memory devices use the full 32/64 bits of the offset, and so we cannot
+ * check against negative addresses: they are ok. The return value is weird,
+ * though, in that case (0).
+ *
+ * also note that seeking relative to the "end of file" isn't supported:
+ * it has no meaning, so it returns -EINVAL.
+ */
+static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
+{
+	switch (orig) {
+		case 0:
+			file->f_pos = offset;
+			return file->f_pos;
+		case 1:
+			file->f_pos += offset;
+			return file->f_pos;
+		default:
+			return -EINVAL;
+	}
+	if (file->f_pos < 0)
+		return 0;
+	return file->f_pos;
+}
+
+#define write_kmem	write_mem
+#define mmap_kmem	mmap_mem
+#define zero_lseek	null_lseek
+#define write_zero	write_null
+
+static struct file_operations ram_fops = {
+	memory_lseek,
+	read_ram,
+	write_ram,
+	NULL,		/* ram_readdir */
+	NULL,		/* ram_select */
+	NULL,		/* ram_ioctl */
+	NULL,		/* ram_mmap */
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* fsync */
+};
+
+static struct file_operations mem_fops = {
+	memory_lseek,
+	read_mem,
+	write_mem,
+	NULL,		/* mem_readdir */
+	NULL,		/* mem_select */
+	NULL,		/* mem_ioctl */
+	mmap_mem,
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* fsync */
+};
+
+static struct file_operations kmem_fops = {
+	memory_lseek,
+	read_kmem,
+	write_kmem,
+	NULL,		/* kmem_readdir */
+	NULL,		/* kmem_select */
+	NULL,		/* kmem_ioctl */
+	mmap_kmem,
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* fsync */
+};
+
+static struct file_operations null_fops = {
+	null_lseek,
+	read_null,
+	write_null,
+	NULL,		/* null_readdir */
+	NULL,		/* null_select */
+	NULL,		/* null_ioctl */
+	NULL,		/* null_mmap */
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* fsync */
+};
+
+static struct file_operations port_fops = {
+	memory_lseek,
+	read_port,
+	write_port,
+	NULL,		/* port_readdir */
+	NULL,		/* port_select */
+	NULL,		/* port_ioctl */
+	NULL,		/* port_mmap */
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* fsync */
+};
+
+static struct file_operations zero_fops = {
+	zero_lseek,
+	read_zero,
+	write_zero,
+	NULL,		/* zero_readdir */
+	NULL,		/* zero_select */
+	NULL,		/* zero_ioctl */
+	mmap_zero,
+	NULL,		/* no special open code */
+	NULL		/* no special release code */
+};
+
+static struct file_operations full_fops = {
+	memory_lseek,
+	read_full,
+	write_full,
+	NULL,		/* full_readdir */
+	NULL,		/* full_select */
+	NULL,		/* full_ioctl */
+	NULL,		/* full_mmap */
+	NULL,		/* no special open code */
+	NULL		/* no special release code */
+};
+
+static struct file_operations random_fops = {
+	memory_lseek,
+	read_random,
+	write_random,
+	NULL,		/* random_readdir */
+	NULL,		/* random_select */
+	random_ioctl,
+	NULL,		/* random_mmap */
+	NULL,		/* no special open code */
+	NULL		/* no special release code */
+};
+
+static struct file_operations urandom_fops = {
+	memory_lseek,
+	read_random_unlimited,
+	write_random,
+	NULL,		/* urandom_readdir */
+	NULL,		/* urandom_select */
+	random_ioctl,
+	NULL,		/* urandom_mmap */
+	NULL,		/* no special open code */
+	NULL		/* no special release code */
+};
+
+static int memory_open(struct inode * inode, struct file * filp)
+{
+	switch (MINOR(inode->i_rdev)) {
+		case 0:
+			filp->f_op = &ram_fops;
+			break;
+		case 1:
+			filp->f_op = &mem_fops;
+			break;
+		case 2:
+			filp->f_op = &kmem_fops;
+			break;
+		case 3:
+			filp->f_op = &null_fops;
+			break;
+		case 4:
+			filp->f_op = &port_fops;
+			break;
+		case 5:
+			filp->f_op = &zero_fops;
+			break;
+		case 7:
+			filp->f_op = &full_fops;
+			break;
+		case 8:
+			filp->f_op = &random_fops;
+			break;
+		case 9:
+			filp->f_op = &urandom_fops;
+			break;
+		default:
+			return -ENODEV;
+	}
+	if (filp->f_op && filp->f_op->open)
+		return filp->f_op->open(inode,filp);
+	return 0;
+}
+
+static struct file_operations memory_fops = {
+	NULL,		/* lseek */
+	NULL,		/* read */
+	NULL,		/* write */
+	NULL,		/* readdir */
+	NULL,		/* select */
+	NULL,		/* ioctl */
+	NULL,		/* mmap */
+	memory_open,	/* just a selector for the real open */
+	NULL,		/* release */
+	NULL		/* fsync */
+};
+
+int chr_dev_init(void)
+{
+	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
+		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
+	rand_initialize();
+	tty_init();
+#ifdef CONFIG_PRINTER
+	lp_init();
+#endif
+#ifdef CONFIG_MOUSE
+	mouse_init();
+#endif
+	return 0;
+}
diff -urNwbB linux/arch/arm/drivers/char/mouse.c linux.arm/arch/arm/drivers/char/mouse.c
--- linux/arch/arm/drivers/char/mouse.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/mouse.c	Sat Feb 24 09:49:56 1996
@@ -0,0 +1,116 @@
+/*
+ * linux/arch/arm/drivers/char/mouse.c
+ *
+ * Generic mouse open routine by Johan Myreen
+ *
+ * Based on code from Linus
+ *
+ * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
+ *   changes incorporated into 0.97pl4
+ *   by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
+ *   See busmouse.c for particulars.
+ *
+ * Made things a lot mode modular - easy to compile in just one or two
+ * of the mouse drivers, as they are now completely independent. Linus.
+ *
+ * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/mouse.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+
+/*
+ * Head entry for the doubly linked mouse list
+ */
+static struct mouse mouse_list = { 0, "head", NULL, &mouse_list, &mouse_list };
+
+extern int arch_mouse_init(void);
+extern struct file_operations arch_mouse_fops;
+
+static int mouse_open(struct inode * inode, struct file * file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct mouse *c = mouse_list.next;
+	file->f_op = NULL;
+
+	while (c != &mouse_list) {
+		if (c->minor == minor) {
+			file->f_op = c->fops;
+			break;
+		}
+		c = c->next;
+	}
+
+	if (file->f_op == NULL)
+		return -ENODEV;
+
+        return file->f_op->open(inode,file);
+}
+
+static struct file_operations mouse_fops = {
+        NULL,		/* seek */
+	NULL,		/* read */
+	NULL,		/* write */
+	NULL,		/* readdir */
+	NULL,		/* select */
+	NULL,		/* ioctl */
+	NULL,		/* mmap */
+        mouse_open,
+        NULL		/* release */
+};
+
+int mouse_register(struct mouse * mouse)
+{
+	if (mouse->next || mouse->prev)
+		return -EBUSY;
+	MOD_INC_USE_COUNT;
+	mouse->next = &mouse_list;
+	mouse->prev = mouse_list.prev;
+	mouse->prev->next = mouse;
+	mouse->next->prev = mouse;
+	return 0;
+}
+
+int mouse_deregister(struct mouse * mouse)
+{
+	if (!mouse->next || !mouse->prev)
+		return -EINVAL;
+	MOD_DEC_USE_COUNT;
+	mouse->prev->next = mouse->next;
+	mouse->next->prev = mouse->prev;
+	mouse->next = NULL;
+	mouse->prev = NULL;
+	return 0;
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+#define mouse_init init_module
+#endif
+
+int mouse_init(void)
+{
+#ifdef CONFIG_KBDMOUSE
+	arch_mouse_init();
+#endif
+	if (register_chrdev(MOUSE_MAJOR, "mouse", &mouse_fops))
+	{
+		printk("unable to get major %d for mouse devices\n",
+		       MOUSE_MAJOR);
+		return -EIO;
+	}
+	return 0;
+}
diff -urNwbB linux/arch/arm/drivers/char/n_tty.c linux.arm/arch/arm/drivers/char/n_tty.c
--- linux/arch/arm/drivers/char/n_tty.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/n_tty.c	Sun Mar  3 12:46:05 1996
@@ -0,0 +1,1019 @@
+/*
+ * n_tty.c --- implements the N_TTY line discipline.
+ * 
+ * This code used to be in tty_io.c, but things are getting hairy
+ * enough that it made sense to split things off.  (The N_TTY
+ * processing has changed so much that it's hardly recognizable,
+ * anyway...)
+ *
+ * Note that the open routine for N_TTY is guaranteed never to return
+ * an error.  This is because Linux will fall back to setting a line
+ * to N_TTY if it can not switch to any other line discipline.  
+ *
+ * Written by Theodore Ts'o, Copyright 1994.
+ * 
+ * This file also contains code originally written by Linus Torvalds,
+ * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994.
+ * 
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ *
+ * Altered for ARM to reduce memory usage.
+ */
+
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/* number of characters left in xmit buffer before select has we have room */
+#define WAKEUP_CHARS 256
+
+/*
+ * This defines the low- and high-watermarks for throttling and
+ * unthrottling the TTY driver.  These watermarks are used for
+ * controlling the space in the read buffer.
+ */
+#define TTY_THRESHOLD_THROTTLE		(N_TTY_BUF_SIZE - 128)
+#define TTY_THRESHOLD_UNTHROTTLE 	128
+
+static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
+{
+	if (tty->read_cnt < N_TTY_BUF_SIZE) {
+		tty->read_buf[tty->read_head] = c;
+		tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
+		tty->read_cnt++;
+	}
+}
+
+/*
+ * Flush the input buffer
+ */
+void n_tty_flush_buffer(struct tty_struct * tty)
+{
+	tty->read_head = tty->read_tail = tty->read_cnt = 0;
+	tty->canon_head = tty->canon_data = tty->erasing = 0;
+	memset(&tty->read_flags, 0, sizeof tty->read_flags);
+	
+	if (!tty->link)
+		return;
+
+	if (tty->driver.unthrottle)
+		(tty->driver.unthrottle)(tty);
+	if (tty->link->packet) {
+		tty->ctrl_status |= TIOCPKT_FLUSHREAD;
+		wake_up_interruptible(&tty->link->read_wait);
+	}
+}
+
+/*
+ * Return number of characters buffered to be delivered to user
+ */
+int n_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	return tty->read_cnt;
+}
+
+/*
+ * Perform OPOST processing.  Returns -1 when the output device is
+ * full and the character must be retried.
+ */
+static int opost(unsigned char c, struct tty_struct *tty)
+{
+	int	space, spaces;
+
+	space = tty->driver.write_room(tty);
+	if (!space)
+		return -1;
+
+	if (O_OPOST(tty)) {
+		switch (c) {
+		case '\n':
+			if (O_ONLRET(tty))
+				tty->column = 0;
+			if (O_ONLCR(tty)) {
+				if (space < 2)
+					return -1;
+				tty->driver.put_char(tty, '\r');
+				tty->column = 0;
+			}
+			tty->canon_column = tty->column;
+			break;
+		case '\r':
+			if (O_ONOCR(tty) && tty->column == 0)
+				return 0;
+			if (O_OCRNL(tty)) {
+				c = '\n';
+				if (O_ONLRET(tty))
+					tty->canon_column = tty->column = 0;
+				break;
+			}
+			tty->canon_column = tty->column = 0;
+			break;
+		case '\t':
+			spaces = 8 - (tty->column & 7);
+			if (O_TABDLY(tty) == XTABS) {
+				if (space < spaces)
+					return -1;
+				tty->column += spaces;
+				tty->driver.write(tty, 0, "        ", spaces);
+				return 0;
+			}
+			tty->column += spaces;
+			break;
+		case '\b':
+			if (tty->column > 0)
+				tty->column--;
+			break;
+		default:
+			if (O_OLCUC(tty))
+				c = toupper(c);
+			if (!iscntrl(c))
+				tty->column++;
+			break;
+		}
+	}
+	tty->driver.put_char(tty, c);
+	return 0;
+}
+
+static inline void put_char(unsigned char c, struct tty_struct *tty)
+{
+	tty->driver.put_char(tty, c);
+}
+
+/* Must be called only when L_ECHO(tty) is true. */
+
+static void echo_char(unsigned char c, struct tty_struct *tty)
+{
+	if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
+		put_char('^', tty);
+		put_char(c ^ 0100, tty);
+		tty->column += 2;
+	} else
+		opost(c, tty);
+}
+
+static inline void finish_erasing(struct tty_struct *tty)
+{
+	if (tty->erasing) {
+		put_char('/', tty);
+		tty->column += 2;
+		tty->erasing = 0;
+	}
+}
+
+static void eraser(unsigned char c, struct tty_struct *tty)
+{
+	enum { ERASE, WERASE, KILL } kill_type;
+	int head, seen_alnums;
+
+	if (tty->read_head == tty->canon_head) {
+		/* opost('\a', tty); */		/* what do you think? */
+		return;
+	}
+	if (c == ERASE_CHAR(tty))
+		kill_type = ERASE;
+	else if (c == WERASE_CHAR(tty))
+		kill_type = WERASE;
+	else {
+		if (!L_ECHO(tty)) {
+			tty->read_cnt -= ((tty->read_head - tty->canon_head) &
+					  (N_TTY_BUF_SIZE - 1));
+			tty->read_head = tty->canon_head;
+			return;
+		}
+		if (!L_ECHOK(tty) || !L_ECHOKE(tty)) {
+			tty->read_cnt -= ((tty->read_head - tty->canon_head) &
+					  (N_TTY_BUF_SIZE - 1));
+			tty->read_head = tty->canon_head;
+			finish_erasing(tty);
+			echo_char(KILL_CHAR(tty), tty);
+			/* Add a newline if ECHOK is on and ECHOKE is off. */
+			if (L_ECHOK(tty))
+				opost('\n', tty);
+			return;
+		}
+		kill_type = KILL;
+	}
+
+	seen_alnums = 0;
+	while (tty->read_head != tty->canon_head) {
+		head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1);
+		c = tty->read_buf[head];
+		if (kill_type == WERASE) {
+			/* Equivalent to BSD's ALTWERASE. */
+			if (isalnum(c) || c == '_')
+				seen_alnums++;
+			else if (seen_alnums)
+				break;
+		}
+		tty->read_head = head;
+		tty->read_cnt--;
+		if (L_ECHO(tty)) {
+			if (L_ECHOPRT(tty)) {
+				if (!tty->erasing) {
+					put_char('\\', tty);
+					tty->column++;
+					tty->erasing = 1;
+				}
+				echo_char(c, tty);
+			} else if (!L_ECHOE(tty)) {
+				echo_char(ERASE_CHAR(tty), tty);
+			} else if (c == '\t') {
+				unsigned int col = tty->canon_column;
+				unsigned long tail = tty->canon_head;
+
+				/* Find the column of the last char. */
+				while (tail != tty->read_head) {
+					c = tty->read_buf[tail];
+					if (c == '\t')
+						col = (col | 7) + 1;
+					else if (iscntrl(c)) {
+						if (L_ECHOCTL(tty))
+							col += 2;
+					} else
+						col++;
+					tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+				}
+
+				/* Now backup to that column. */
+				while (tty->column > col) {
+					/* Can't use opost here. */
+					put_char('\b', tty);
+					tty->column--;
+				}
+			} else {
+				if (iscntrl(c) && L_ECHOCTL(tty)) {
+					put_char('\b', tty);
+					put_char(' ', tty);
+					put_char('\b', tty);
+					tty->column--;
+				}
+				if (!iscntrl(c) || L_ECHOCTL(tty)) {
+					put_char('\b', tty);
+					put_char(' ', tty);
+					put_char('\b', tty);
+					tty->column--;
+				}
+			}
+		}
+		if (kill_type == ERASE)
+			break;
+	}
+	if (tty->read_head == tty->canon_head)
+		finish_erasing(tty);
+}
+
+static void isig(int sig, struct tty_struct *tty)
+{
+	if (tty->pgrp > 0)
+		kill_pg(tty->pgrp, sig, 1);
+	if (!L_NOFLSH(tty)) {
+		n_tty_flush_buffer(tty);
+		if (tty->driver.flush_buffer)
+			tty->driver.flush_buffer(tty);
+	}
+}
+
+static inline void n_tty_receive_break(struct tty_struct *tty)
+{
+	if (I_IGNBRK(tty))
+		return;
+	if (I_BRKINT(tty)) {
+		isig(SIGINT, tty);
+		return;
+	}
+	if (I_PARMRK(tty)) {
+		put_tty_queue('\377', tty);
+		put_tty_queue('\0', tty);
+	}
+	put_tty_queue('\0', tty);
+	wake_up_interruptible(&tty->read_wait);
+}
+
+static inline void n_tty_receive_overrun(struct tty_struct *tty)
+{
+	char buf[64];
+
+	tty->num_overrun++;
+	if (tty->overrun_time < (jiffies - HZ)) {
+		printk("%s: %d input overrun(s)\n", _tty_name(tty, buf),
+		       tty->num_overrun);
+		tty->overrun_time = jiffies;
+		tty->num_overrun = 0;
+	}
+}
+
+static inline void n_tty_receive_parity_error(struct tty_struct *tty,
+					      unsigned char c)
+{
+	if (I_IGNPAR(tty)) {
+		return;
+	}
+	if (I_PARMRK(tty)) {
+		put_tty_queue('\377', tty);
+		put_tty_queue('\0', tty);
+		put_tty_queue(c, tty);
+	} else
+		put_tty_queue('\0', tty);
+	wake_up_interruptible(&tty->read_wait);
+}
+
+static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+{
+	if (tty->raw) {
+		put_tty_queue(c, tty);
+		return;
+	}
+	
+	if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) {
+		start_tty(tty);
+		return;
+	}
+	
+	if (I_ISTRIP(tty))
+		c &= 0x7f;
+	if (I_IUCLC(tty) && L_IEXTEN(tty))
+		c=tolower(c);
+
+	if (tty->closing) {
+		if (I_IXON(tty)) {
+			if (c == START_CHAR(tty))
+				start_tty(tty);
+			else if (c == STOP_CHAR(tty))
+				stop_tty(tty);
+		}
+		return;
+	}
+
+	/*
+	 * If the previous character was LNEXT, or we know that this
+	 * character is not one of the characters that we'll have to
+	 * handle specially, do shortcut processing to speed things
+	 * up.
+	 */
+	if (!test_bit(c, &tty->process_char_map) || tty->lnext) {
+		finish_erasing(tty);
+		tty->lnext = 0;
+		if (L_ECHO(tty)) {
+			if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+				put_char('\a', tty); /* beep if no space */
+				return;
+			}
+			/* Record the column of first canon char. */
+			if (tty->canon_head == tty->read_head)
+				tty->canon_column = tty->column;
+			echo_char(c, tty);
+		}
+		if (I_PARMRK(tty) && c == (unsigned char) '\377')
+			put_tty_queue(c, tty);
+		put_tty_queue(c, tty);
+		return;
+	}
+		
+	if (c == '\r') {
+		if (I_IGNCR(tty))
+			return;
+		if (I_ICRNL(tty))
+			c = '\n';
+	} else if (c == '\n' && I_INLCR(tty))
+		c = '\r';
+	if (I_IXON(tty)) {
+		if (c == START_CHAR(tty)) {
+			start_tty(tty);
+			return;
+		}
+		if (c == STOP_CHAR(tty)) {
+			stop_tty(tty);
+			return;
+		}
+	}
+	if (L_ISIG(tty)) {
+		if (c == INTR_CHAR(tty)) {
+			isig(SIGINT, tty);
+			return;
+		}
+		if (c == QUIT_CHAR(tty)) {
+			isig(SIGQUIT, tty);
+			return;
+		}
+		if (c == SUSP_CHAR(tty)) {
+			if (!is_orphaned_pgrp(tty->pgrp))
+				isig(SIGTSTP, tty);
+			return;
+		}
+	}
+	if (L_ICANON(tty)) {
+		if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
+		    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
+			eraser(c, tty);
+			return;
+		}
+		if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
+			tty->lnext = 1;
+			if (L_ECHO(tty)) {
+				finish_erasing(tty);
+				if (L_ECHOCTL(tty)) {
+					put_char('^', tty);
+					put_char('\b', tty);
+				}
+			}
+			return;
+		}
+		if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
+		    L_IEXTEN(tty)) {
+			unsigned long tail = tty->canon_head;
+
+			finish_erasing(tty);
+			echo_char(c, tty);
+			opost('\n', tty);
+			while (tail != tty->read_head) {
+				echo_char(tty->read_buf[tail], tty);
+				tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+			}
+			return;
+		}
+		if (c == '\n') {
+			if (L_ECHO(tty) || L_ECHONL(tty)) {
+				if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+					put_char('\a', tty);
+					return;
+				}
+				opost('\n', tty);
+			}
+			goto handle_newline;
+		}
+		if (c == EOF_CHAR(tty)) {
+		        if (tty->canon_head != tty->read_head)
+			        set_bit(TTY_PUSH, &tty->flags);
+			c = __DISABLED_CHAR;
+			goto handle_newline;
+		}
+		if ((c == EOL_CHAR(tty)) ||
+		    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
+			/*
+			 * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
+			 */
+			if (L_ECHO(tty)) {
+				if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+					put_char('\a', tty);
+					return;
+				}
+				/* Record the column of first canon char. */
+				if (tty->canon_head == tty->read_head)
+					tty->canon_column = tty->column;
+				echo_char(c, tty);
+			}
+			/*
+			 * XXX does PARMRK doubling happen for
+			 * EOL_CHAR and EOL2_CHAR?
+			 */
+			if (I_PARMRK(tty) && c == (unsigned char) '\377')
+				put_tty_queue(c, tty);
+
+		handle_newline:
+			set_bit(tty->read_head, &tty->read_flags);
+			put_tty_queue(c, tty);
+			tty->canon_head = tty->read_head;
+			tty->canon_data++;
+			if (tty->fasync)
+				kill_fasync(tty->fasync, SIGIO);
+			if (tty->read_wait)
+				wake_up_interruptible(&tty->read_wait);
+			return;
+		}
+	}
+	
+	finish_erasing(tty);
+	if (L_ECHO(tty)) {
+		if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+			put_char('\a', tty); /* beep if no space */
+			return;
+		}
+		if (c == '\n')
+			opost('\n', tty);
+		else {
+			/* Record the column of first canon char. */
+			if (tty->canon_head == tty->read_head)
+				tty->canon_column = tty->column;
+			echo_char(c, tty);
+		}
+	}
+
+	if (I_PARMRK(tty) && c == (unsigned char) '\377')
+		put_tty_queue(c, tty);
+
+	put_tty_queue(c, tty);
+}	
+
+static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+			      char *fp, int count)
+{
+	const unsigned char *p;
+	char *f, flags = 0;
+	int	i;
+
+	if (!tty->read_buf)
+		return;
+
+	if (tty->real_raw) {
+		i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
+				   N_TTY_BUF_SIZE - tty->read_head));
+		memcpy(tty->read_buf + tty->read_head, cp, i);
+		tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
+		tty->read_cnt += i;
+		cp += i;
+		count -= i;
+
+		i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
+			       N_TTY_BUF_SIZE - tty->read_head));
+		memcpy(tty->read_buf + tty->read_head, cp, i);
+		tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
+		tty->read_cnt += i;
+	} else {
+		for (i=count, p = cp, f = fp; i; i--, p++) {
+			if (f)
+				flags = *f++;
+			switch (flags) {
+			case TTY_NORMAL:
+				n_tty_receive_char(tty, *p);
+				break;
+			case TTY_BREAK:
+				n_tty_receive_break(tty);
+				break;
+			case TTY_PARITY:
+			case TTY_FRAME:
+				n_tty_receive_parity_error(tty, *p);
+				break;
+			case TTY_OVERRUN:
+				n_tty_receive_overrun(tty);
+				break;
+			default:
+				printk("%s: unknown flag %d\n", tty_name(tty),
+				       flags);
+				break;
+			}
+		}
+		if (tty->driver.flush_chars)
+			tty->driver.flush_chars(tty);
+	}
+
+	if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
+		if (tty->fasync)
+			kill_fasync(tty->fasync, SIGIO);
+		if (tty->read_wait)
+			wake_up_interruptible(&tty->read_wait);
+	}
+
+	if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) &&
+	    tty->driver.throttle &&
+	    !set_bit(TTY_THROTTLED, &tty->flags))
+		tty->driver.throttle(tty);
+}
+
+static int n_tty_receive_room(struct tty_struct *tty)
+{
+	int	left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
+
+	/*
+	 * If we are doing input canonicalization, and there are no
+	 * pending newlines, let characters through without limit, so
+	 * that erase characters will be handled.  Other excess
+	 * characters will be beeped.
+	 */
+	if (tty->icanon && !tty->canon_data)
+		return N_TTY_BUF_SIZE;
+
+	if (left > 0)
+		return left;
+	return 0;
+}
+
+int is_ignored(int sig)
+{
+	return ((current->blocked & (1<<(sig-1))) ||
+	        (current->sig->action[sig-1].sa_handler == SIG_IGN));
+}
+
+static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
+{
+	if (!tty)
+		return;
+	
+	tty->icanon = (L_ICANON(tty) != 0);
+	if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
+	    I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
+	    I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
+	    I_PARMRK(tty)) {
+		cli();
+		memset(tty->process_char_map, 0, 256/8);
+
+		if (I_IGNCR(tty) || I_ICRNL(tty))
+			set_bit('\r', &tty->process_char_map);
+		if (I_INLCR(tty))
+			set_bit('\n', &tty->process_char_map);
+
+		if (L_ICANON(tty)) {
+			set_bit(ERASE_CHAR(tty), &tty->process_char_map);
+			set_bit(KILL_CHAR(tty), &tty->process_char_map);
+			set_bit(EOF_CHAR(tty), &tty->process_char_map);
+			set_bit('\n', &tty->process_char_map);
+			set_bit(EOL_CHAR(tty), &tty->process_char_map);
+			if (L_IEXTEN(tty)) {
+				set_bit(WERASE_CHAR(tty),
+					&tty->process_char_map);
+				set_bit(LNEXT_CHAR(tty),
+					&tty->process_char_map);
+				set_bit(EOL2_CHAR(tty),
+					&tty->process_char_map);
+				if (L_ECHO(tty))
+					set_bit(REPRINT_CHAR(tty),
+						&tty->process_char_map);
+			}
+		}
+		if (I_IXON(tty)) {
+			set_bit(START_CHAR(tty), &tty->process_char_map);
+			set_bit(STOP_CHAR(tty), &tty->process_char_map);
+		}
+		if (L_ISIG(tty)) {
+			set_bit(INTR_CHAR(tty), &tty->process_char_map);
+			set_bit(QUIT_CHAR(tty), &tty->process_char_map);
+			set_bit(SUSP_CHAR(tty), &tty->process_char_map);
+		}
+		clear_bit(__DISABLED_CHAR, &tty->process_char_map);
+		sti();
+		tty->raw = 0;
+		tty->real_raw = 0;
+	} else {
+		tty->raw = 1;
+		if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
+		    (I_IGNPAR(tty) || !I_INPCK(tty)) &&
+		    (tty->driver.flags & TTY_DRIVER_REAL_RAW))
+			tty->real_raw = 1;
+		else
+			tty->real_raw = 0;
+	}
+}
+
+static void n_tty_close(struct tty_struct *tty)
+{
+	n_tty_flush_buffer(tty);
+	if (tty->read_buf) {
+		kfree_s (tty->read_buf, N_TTY_BUF_SIZE);
+		tty->read_buf = 0;
+	}
+}
+
+static int n_tty_open(struct tty_struct *tty)
+{
+	if (!tty)
+		return -EINVAL;
+
+	if (!tty->read_buf) {
+		tty->read_buf = (unsigned char *)
+			kmalloc (N_TTY_BUF_SIZE, intr_count ? GFP_ATOMIC : GFP_KERNEL);
+		if (!tty->read_buf)
+			return -ENOMEM;
+	}
+	memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
+	tty->read_head = tty->read_tail = tty->read_cnt = 0;
+	memset(tty->read_flags, 0, sizeof(tty->read_flags));
+	n_tty_set_termios(tty, 0);
+	tty->minimum_to_wake = 1;
+	tty->closing = 0;
+	return 0;
+}
+
+static inline int input_available_p(struct tty_struct *tty, int amt)
+{
+	if (L_ICANON(tty)) {
+		if (tty->canon_data)
+			return 1;
+	} else if (tty->read_cnt >= (amt ? amt : 1))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Helper function to speed up read_chan.  It is only called when
+ * ICANON is off; it copies characters straight from the tty queue to
+ * user space directly.  It can be profitably called twice; once to
+ * drain the space from the tail pointer to the (physical) end of the
+ * buffer, and once to drain the space from the (physical) beginning of
+ * the buffer to head pointer.
+ */
+static inline void copy_from_read_buf(struct tty_struct *tty,
+				      unsigned char **b,
+				      unsigned int *nr)
+
+{
+	int	n;
+
+	n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
+	if (!n)
+		return;
+	memcpy_tofs(*b, &tty->read_buf[tty->read_tail], n);
+	tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
+	tty->read_cnt -= n;
+	*b += n;
+	*nr -= n;
+}
+
+static int read_chan(struct tty_struct *tty, struct file *file,
+		     unsigned char *buf, unsigned int nr)
+{
+	struct wait_queue wait = { current, NULL };
+	int c;
+	unsigned char *b = buf;
+	int minimum, time;
+	int retval = 0;
+	int size;
+
+do_it_again:
+
+	if (!tty->read_buf) {
+		printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
+		return -EIO;
+	}
+
+	/* Job control check -- must be done at start and after
+	   every sleep (POSIX.1 7.1.1.4). */
+	/* NOTE: not yet done after every sleep pending a thorough
+	   check of the logic of this change. -- jlc */
+	/* don't stop on /dev/console */
+	if (file->f_inode->i_rdev != CONSOLE_DEV &&
+	    current->tty == tty) {
+		if (tty->pgrp <= 0)
+			printk("read_chan: tty->pgrp <= 0!\n");
+		else if (current->pgrp != tty->pgrp) {
+			if (is_ignored(SIGTTIN) ||
+			    is_orphaned_pgrp(current->pgrp))
+				return -EIO;
+			kill_pg(current->pgrp, SIGTTIN, 1);
+			return -ERESTARTSYS;
+		}
+	}
+
+	if (L_ICANON(tty)) {
+		minimum = time = 0;
+		current->timeout = (unsigned long) -1;
+	} else {
+		time = (HZ / 10) * TIME_CHAR(tty);
+		minimum = MIN_CHAR(tty);
+		if (minimum) {
+		  	current->timeout = (unsigned long) -1;
+			if (time)
+				tty->minimum_to_wake = 1;
+			else if (!tty->read_wait ||
+				 (tty->minimum_to_wake > minimum))
+				tty->minimum_to_wake = minimum;
+		} else {
+			if (time) {
+				current->timeout = time + jiffies;
+				time = 0;
+			} else
+				current->timeout = 0;
+			tty->minimum_to_wake = minimum = 1;
+		}
+	}
+
+	add_wait_queue(&tty->read_wait, &wait);
+	while (1) {
+		/* First test for status change. */
+		if (tty->packet && tty->link->ctrl_status) {
+			if (b != buf)
+				break;
+			put_user(tty->link->ctrl_status, b++);
+			tty->link->ctrl_status = 0;
+			break;
+		}
+		/* This statement must be first before checking for input
+		   so that any interrupt will set the state back to
+		   TASK_RUNNING. */
+		current->state = TASK_INTERRUPTIBLE;
+		
+		if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
+		    ((minimum - (b - buf)) >= 1))
+			tty->minimum_to_wake = (minimum - (b - buf));
+		
+		if (!input_available_p(tty, 0)) {
+			if (tty->flags & (1 << TTY_SLAVE_CLOSED)) {
+				retval = -EIO;
+				break;
+			}
+			if (tty_hung_up_p(file))
+				break;
+			if (!current->timeout)
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				break;
+			}
+			if (current->signal & ~current->blocked) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+			schedule();
+			continue;
+		}
+		current->state = TASK_RUNNING;
+
+		/* Deal with packet mode. */
+		if (tty->packet && b == buf) {
+			put_user(TIOCPKT_DATA, b++);
+			nr--;
+		}
+
+		if (L_ICANON(tty)) {
+			while (1) {
+				int eol;
+
+				disable_bh(TQUEUE_BH);
+				if (!tty->read_cnt) {
+					enable_bh(TQUEUE_BH);
+					break;
+				}
+				eol = clear_bit(tty->read_tail,
+						&tty->read_flags);
+				c = tty->read_buf[tty->read_tail];
+				tty->read_tail = ((tty->read_tail+1) &
+						  (N_TTY_BUF_SIZE-1));
+				tty->read_cnt--;
+				enable_bh(TQUEUE_BH);
+				if (!eol) {
+					put_user(c, b++);
+					if (--nr)
+						continue;
+					break;
+				}
+				if (--tty->canon_data < 0) {
+					tty->canon_data = 0;
+				}
+				if (c != __DISABLED_CHAR) {
+					put_user(c, b++);
+					nr--;
+				}
+				break;
+			}
+		} else {
+			disable_bh(TQUEUE_BH);
+			copy_from_read_buf(tty, &b, &nr);
+			copy_from_read_buf(tty, &b, &nr);
+			enable_bh(TQUEUE_BH);
+		}
+
+		/* If there is enough space in the read buffer now, let the
+		   low-level driver know. */
+		if (tty->driver.unthrottle &&
+		    (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE)
+		    && clear_bit(TTY_THROTTLED, &tty->flags))
+			tty->driver.unthrottle(tty);
+
+		if (b - buf >= minimum || !nr)
+			break;
+		if (time)
+			current->timeout = time + jiffies;
+	}
+	remove_wait_queue(&tty->read_wait, &wait);
+
+	if (!tty->read_wait)
+		tty->minimum_to_wake = minimum;
+
+	current->state = TASK_RUNNING;
+	current->timeout = 0;
+	size = b - buf;
+	if (size && nr)
+	        clear_bit(TTY_PUSH, &tty->flags);
+        if (!size && clear_bit(TTY_PUSH, &tty->flags))
+                goto do_it_again;
+	if (!size && !retval)
+	        clear_bit(TTY_PUSH, &tty->flags);
+        return (size ? size : retval);
+}
+
+static int write_chan(struct tty_struct * tty, struct file * file,
+		      const unsigned char * buf, unsigned int nr)
+{
+	struct wait_queue wait = { current, NULL };
+	int c;
+	const unsigned char *b = buf;
+	int retval = 0;
+
+	/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
+	if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+	}
+
+	add_wait_queue(&tty->write_wait, &wait);
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
+			retval = -EIO;
+			break;
+		}
+		if (O_OPOST(tty)) {
+			while (nr > 0) {
+				c = get_user(b);
+				if (opost(c, tty) < 0)
+					break;
+				b++; nr--;
+			}
+			if (tty->driver.flush_chars)
+				tty->driver.flush_chars(tty);
+		} else {
+			c = tty->driver.write(tty, 1, b, nr);
+			b += c;
+			nr -= c;
+		}
+		if (!nr)
+			break;
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&tty->write_wait, &wait);
+	return (b - buf) ? b - buf : retval;
+}
+
+static int normal_select(struct tty_struct * tty, struct inode * inode,
+			 struct file * file, int sel_type, select_table *wait)
+{
+	switch (sel_type) {
+		case SEL_IN:
+			if (input_available_p(tty, TIME_CHAR(tty) ? 0 :
+					      MIN_CHAR(tty)))
+				return 1;
+			/* fall through */
+		case SEL_EX:
+			if (tty->packet && tty->link->ctrl_status)
+				return 1;
+			if (tty->flags & (1 << TTY_SLAVE_CLOSED))
+				return 1;
+			if (tty_hung_up_p(file))
+				return 1;
+			if (!tty->read_wait) {
+				if (MIN_CHAR(tty) && !TIME_CHAR(tty))
+					tty->minimum_to_wake = MIN_CHAR(tty);
+				else
+					tty->minimum_to_wake = 1;
+			}
+			select_wait(&tty->read_wait, wait);
+			return 0;
+		case SEL_OUT:
+			if (tty->driver.chars_in_buffer(tty) < WAKEUP_CHARS)
+				return 1;
+			select_wait(&tty->write_wait, wait);
+			return 0;
+	}
+	return 0;
+}
+
+struct tty_ldisc tty_ldisc_N_TTY = {
+	TTY_LDISC_MAGIC,	/* magic */
+	0,			/* num */
+	0,			/* flags */
+	n_tty_open,		/* open */
+	n_tty_close,		/* close */
+	n_tty_flush_buffer,	/* flush_buffer */
+	n_tty_chars_in_buffer,	/* chars_in_buffer */
+	read_chan,		/* read */
+	write_chan,		/* write */
+	n_tty_ioctl,		/* ioctl */
+	n_tty_set_termios,	/* set_termios */
+	normal_select,		/* select */
+	n_tty_receive_buf,	/* receive_buf */
+	n_tty_receive_room,	/* receive_room */
+	0			/* write_wakeup */
+};
+
diff -urNwbB linux/arch/arm/drivers/char/pty.c linux.arm/arch/arm/drivers/char/pty.c
--- linux/arch/arm/drivers/char/pty.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/pty.c	Sun Mar  3 12:46:45 1996
@@ -0,0 +1,265 @@
+/*
+ *  linux/arch/arm/drivers/char/pty.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Modifications for ARM processor Copyright (C) 1995, 1996 Russell King.
+ */
+
+/*
+ *	pty.c
+ *
+ * This module exports the following pty function:
+ *
+ * 	int  pty_open(struct tty_struct * tty, struct file * filp);
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+struct pty_struct {
+	int	magic;
+	struct wait_queue * open_wait;
+};
+
+#define PTY_MAGIC 0x5001
+#define PTY_BUF_SIZE 4096/2
+/*
+ * tmp_buf is used as a temporary buffer by pty_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a pty write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the PTY's, since it significantly saves memory if
+ * large numbers of PTY's are open.
+ */
+static unsigned char *tmp_buf;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+struct tty_driver pty_driver, pty_slave_driver;
+static int pty_refcount;
+
+static struct tty_struct *pty_table[NR_PTYS];
+static struct termios *pty_termios[NR_PTYS];
+static struct termios *pty_termios_locked[NR_PTYS];
+static struct tty_struct *ttyp_table[NR_PTYS];
+static struct termios *ttyp_termios[NR_PTYS];
+static struct termios *ttyp_termios_locked[NR_PTYS];
+static struct pty_struct pty_state[NR_PTYS];
+
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+
+static void pty_close(struct tty_struct * tty, struct file * filp)
+{
+	if (!tty)
+		return;
+	if (tty->driver.subtype == PTY_TYPE_MASTER) {
+		if (tty->count > 1)
+			printk("master pty_close: count = %d!!\n", tty->count);
+	} else {
+		if (tty->count > 2)
+			return;
+	}
+	wake_up_interruptible(&tty->read_wait);
+	wake_up_interruptible(&tty->write_wait);
+	if (!tty->link)
+		return;
+	wake_up_interruptible(&tty->link->read_wait);
+	wake_up_interruptible(&tty->link->write_wait);
+	if (tty->driver.subtype == PTY_TYPE_MASTER)
+		tty_hangup(tty->link);
+	else {
+		start_tty(tty);
+		set_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
+	}
+}
+
+/*
+ * The unthrottle routine is called by the line discipline to signal
+ * that it can receive more characters.  For PTY's, the TTY_THROTTLED
+ * flag is always set, to force the line discipline to always call the
+ * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
+ * characters in the queue.  This is necessary since each time this
+ * happens, we need to wake up any sleeping processes that could be
+ * (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
+ * for the pty buffer to be drained.
+ */
+static void pty_unthrottle(struct tty_struct * tty)
+{
+	struct tty_struct *o_tty = tty->link;
+
+	if (!o_tty)
+		return;
+
+	if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    o_tty->ldisc.write_wakeup)
+		(o_tty->ldisc.write_wakeup)(o_tty);
+	wake_up_interruptible(&o_tty->write_wait);
+	set_bit(TTY_THROTTLED, &tty->flags);
+}
+
+static int pty_write(struct tty_struct * tty, int from_user,
+		       const unsigned char *buf, int count)
+{
+	struct tty_struct *to = tty->link;
+	int	c=0, n, r;
+	char	*temp_buffer;
+
+	if (!to || tty->stopped)
+		return 0;
+
+	if (from_user) {
+		down(&tmp_buf_sem);
+		temp_buffer = tmp_buf +
+			((tty->driver.subtype-1) * PTY_BUF_SIZE);
+		while (count > 0) {
+			n = MIN(count, PTY_BUF_SIZE);
+			memcpy_fromfs(temp_buffer, buf, n);
+			r = to->ldisc.receive_room(to);
+			if (r <= 0)
+				break;
+			n = MIN(n, r);
+			to->ldisc.receive_buf(to, temp_buffer, 0, n);
+			buf += n;  c+= n;
+			count -= n;
+		}
+		up(&tmp_buf_sem);
+	} else {
+		c = MIN(count, to->ldisc.receive_room(to));
+		to->ldisc.receive_buf(to, buf, 0, c);
+	}
+
+	return c;
+}
+
+static int pty_write_room(struct tty_struct *tty)
+{
+	struct tty_struct *to = tty->link;
+
+	if (!to || tty->stopped)
+		return 0;
+
+	return to->ldisc.receive_room(to);
+}
+
+static int pty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct tty_struct *to = tty->link;
+
+	if (!to || !to->ldisc.chars_in_buffer)
+		return 0;
+
+	return to->ldisc.chars_in_buffer(to);
+}
+
+static void pty_flush_buffer(struct tty_struct *tty)
+{
+	struct tty_struct *to = tty->link;
+
+	if (!to)
+		return;
+
+	if (to->ldisc.flush_buffer)
+		to->ldisc.flush_buffer(to);
+
+	if (to->packet) {
+		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
+		wake_up_interruptible(&to->read_wait);
+	}
+}
+
+int pty_open(struct tty_struct *tty, struct file * filp)
+{
+	int	line;
+	struct	pty_struct *pty;
+
+	if (!tty || !tty->link)
+		return -ENODEV;
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PTYS))
+		return -ENODEV;
+	pty = pty_state + line;
+	tty->driver_data = pty;
+
+	if (!tmp_buf) {
+		tmp_buf = (unsigned char *) kmalloc(PTY_BUF_SIZE*2,GFP_KERNEL);
+		if (!tmp_buf)
+			return -ENOMEM;
+	}
+
+	if (tty->driver.subtype == PTY_TYPE_SLAVE)
+		clear_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
+	wake_up_interruptible(&pty->open_wait);
+	set_bit(TTY_THROTTLED, &tty->flags);
+	if (filp->f_flags & O_NDELAY)
+		return 0;
+	while (!tty->link->count && !(current->signal & ~current->blocked))
+		interruptible_sleep_on(&pty->open_wait);
+	if (!tty->link->count)
+		return -ERESTARTSYS;
+	return 0;
+}
+
+int pty_init(void)
+{
+	memset(&pty_state, 0, sizeof(pty_state));
+	memset(&pty_driver, 0, sizeof(struct tty_driver));
+	pty_driver.magic = TTY_DRIVER_MAGIC;
+	pty_driver.name = "pty";
+	pty_driver.major = TTY_MAJOR;
+	pty_driver.minor_start = 128;
+	pty_driver.num = NR_PTYS;
+	pty_driver.type = TTY_DRIVER_TYPE_PTY;
+	pty_driver.subtype = PTY_TYPE_MASTER;
+	pty_driver.init_termios = tty_std_termios;
+	pty_driver.init_termios.c_iflag = 0;
+	pty_driver.init_termios.c_oflag = 0;
+	pty_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
+	pty_driver.init_termios.c_lflag = 0;
+	pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
+	pty_driver.refcount = &pty_refcount;
+	pty_driver.table = pty_table;
+	pty_driver.termios = pty_termios;
+	pty_driver.termios_locked = pty_termios_locked;
+	pty_driver.other = &pty_slave_driver;
+
+	pty_driver.open = pty_open;
+	pty_driver.close = pty_close;
+	pty_driver.write = pty_write;
+	pty_driver.write_room = pty_write_room;
+	pty_driver.flush_buffer = pty_flush_buffer;
+	pty_driver.chars_in_buffer = pty_chars_in_buffer;
+	pty_driver.unthrottle = pty_unthrottle;
+
+	pty_slave_driver = pty_driver;
+	pty_slave_driver.name = "ttyp";
+	pty_slave_driver.subtype = PTY_TYPE_SLAVE;
+	pty_slave_driver.minor_start = 192;
+	pty_slave_driver.init_termios = tty_std_termios;
+	pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
+	pty_slave_driver.table = ttyp_table;
+	pty_slave_driver.termios = ttyp_termios;
+	pty_slave_driver.termios_locked = ttyp_termios_locked;
+	pty_slave_driver.other = &pty_driver;
+
+	tmp_buf = 0;
+
+	if (tty_register_driver(&pty_driver))
+		panic("Couldn't register pty driver");
+	if (tty_register_driver(&pty_slave_driver))
+		panic("Couldn't register pty slave driver");
+
+	return 0;
+}
diff -urNwbB linux/arch/arm/drivers/char/selection.h linux.arm/arch/arm/drivers/char/selection.h
--- linux/arch/arm/drivers/char/selection.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/selection.h	Sun Feb 11 09:32:21 1996
@@ -0,0 +1,20 @@
+/*
+ * selection.h
+ *
+ * Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
+ */
+extern int sel_cons;
+
+extern void clear_selection(void);
+extern int set_selection(const unsigned long arg, struct tty_struct *tty);
+extern int paste_selection(struct tty_struct *tty);
+extern int sel_loadlut(const unsigned long arg);
+extern int mouse_reporting(void);
+extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry);
+
+extern unsigned long video_num_columns;
+extern unsigned long video_num_lines;
+extern unsigned long video_size_row;
+
+extern void do_unblank_screen(void);
+
diff -urNwbB linux/arch/arm/drivers/char/serial.c linux.arm/arch/arm/drivers/char/serial.c
--- linux/arch/arm/drivers/char/serial.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/serial.c	Sun Mar  3 12:47:09 1996
@@ -0,0 +1,2715 @@
+/*
+ *  linux/arch/arm/drivers/char/serial.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now
+ *  much more extensible to support other serial cards based on the
+ *  16450/16550A UART's.  Added support for the AST FourPort and the
+ *  Accent Async board.  
+ *
+ *  set_serial_info fixed to set the flags, custom divisor, and uart
+ * 	type fields.  Fix suggested by Michael K. Johnson 12/12/92.
+ *
+ * This module exports the following rs232 io functions:
+ *
+ *	int rs_init(void);
+ * 	int rs_open(struct tty_struct * tty, struct file * filp)
+ *
+ *  Slight modifications for ARM Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/*
+ * Serial driver configuration section.  Here are the various options:
+ *
+ * CONFIG_HUB6
+ *		Enables support for the venerable Bell Technologies
+ *		HUB6 card.
+ *
+ * SERIAL_PARANOIA_CHECK
+ * 		Check the magic number for the async_structure where
+ * 		ever possible.
+ */
+
+#define SERIAL_PARANOIA_CHECK
+#define CONFIG_SERIAL_NOPAUSE_IO
+#define SERIAL_DO_RESTART
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME (10*HZ)
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+  
+/*
+ * IRQ_timeout		- How long the timeout should be for each IRQ
+ * 				should be after the IRQ has been active.
+ */
+
+static struct async_struct *IRQ_ports[16];
+static struct rs_multiport_struct rs_multiport[16];
+static int IRQ_timeout[16];
+static volatile int rs_irq_triggered;
+static volatile int rs_triggered;
+static int rs_wild_int_mask;
+
+static void autoconfig(struct async_struct * info);
+static void change_speed(struct async_struct *info);
+	
+/*
+ * This assumes you have a 1.8432 MHz clock for your UART.
+ *
+ * It'd be nice if someone built a serial card with a 24.576 MHz
+ * clock, since the 16550A is capable of handling a top speed of 1.5
+ * megabits/second; but this requires the faster clock.
+ */
+#define BASE_BAUD ( 1843200 / 16 )
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
+
+struct async_struct rs_table[] = {
+	/* UART CLK   PORT IRQ     FLAGS        */
+	{ 0, BASE_BAUD, 0x3F8,10, STD_COM_FLAGS },	/* ttyS0 */
+	{ 0, BASE_BAUD, 0x2F8,10, STD_COM_FLAGS },	/* ttyS1 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS2 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS3 */
+
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS }, 	/* ttyS4 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS5 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS6 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS7 */
+
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS8 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS9 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS10 */
+	{ 0, BASE_BAUD, 0    , 0, STD_COM_FLAGS },	/* ttyS11 */
+	
+};
+
+#define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct async_struct *info,
+					kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null async_struct for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 0 };
+
+static inline unsigned int serial_in(struct async_struct *info, int offset)
+{
+#ifdef CONFIG_HUB6
+    if (info->hub6) {
+	outb(info->hub6 - 1 + offset, info->port);
+	return inb(info->port+1);
+    } else
+#endif
+	return inb(info->port + offset);
+}
+
+static inline unsigned int serial_inp(struct async_struct *info, int offset)
+{
+#ifdef CONFIG_HUB6
+    if (info->hub6) {
+	outb(info->hub6 - 1 + offset, info->port);
+	return inb_p(info->port+1);
+    } else
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	return inb(info->port + offset);
+#else
+	return inb_p(info->port + offset);
+#endif
+}
+
+static inline void serial_out(struct async_struct *info, int offset, int value)
+{
+#ifdef CONFIG_HUB6
+    if (info->hub6) {
+	outb(info->hub6 - 1 + offset, info->port);
+	outb(value, info->port+1);
+    } else
+#endif
+	outb(value, info->port+offset);
+}
+
+static inline void serial_outp(struct async_struct *info, int offset,
+			       int value)
+{
+#ifdef CONFIG_HUB6
+    if (info->hub6) {
+	outb(info->hub6 - 1 + offset, info->port);
+	outb_p(value, info->port+1);
+    } else
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	outb(value, info->port+offset);
+#else
+    	outb_p(value, info->port+offset);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+	
+	save_flags(flags); cli();
+	if (info->IER & UART_IER_THRI) {
+		info->IER &= ~UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+	restore_flags(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_start"))
+		return;
+	
+	save_flags(flags); cli();
+	if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+	restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This is the serial driver's interrupt routine while we are probing
+ * for submarines.
+ */
+static void rs_probe(int irq, struct pt_regs * regs)
+{
+	rs_irq_triggered = irq;
+	rs_triggered |= 1 << irq;
+	return;
+}
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct async_struct *info,
+				  int event)
+{
+	info->event |= 1 << event;
+	queue_task_irq_off(&info->tqueue, &tq_serial);
+	mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(struct async_struct *info,
+				 int *status)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char ch;
+	int ignored = 0;
+
+	do {
+		ch = serial_inp(info, UART_RX);
+		if (*status & info->ignore_status_mask) {
+			if (++ignored > 100)
+				break;
+			goto ignore_char;
+		}
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			break;
+		tty->flip.count++;
+		if (*status & (UART_LSR_BI)) {
+			printk("handling break....");
+			*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+			if (info->flags & ASYNC_SAK)
+				do_SAK(tty);
+		} else if (*status & UART_LSR_PE)
+			*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+		else if (*status & UART_LSR_FE)
+			*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+		else if (*status & UART_LSR_OE) 
+			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+		else
+			*tty->flip.flag_buf_ptr++ = 0;
+		*tty->flip.char_buf_ptr++ = ch;
+	ignore_char:
+		*status = serial_inp(info, UART_LSR) & info->read_status_mask;
+	} while (*status & UART_LSR_DR);
+{ unsigned long flags; save_flags (flags); if (!(flags & 0x08000000)) printk ("SERIAL IRQ ON!\n"); }
+	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+#ifdef SERIAL_DEBUG_INTR
+	printk("DR...");
+#endif
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
+{
+	int count;
+	
+	if (info->x_char) {
+		serial_outp(info, UART_TX, info->x_char);
+		info->x_char = 0;
+		if (intr_done)
+			*intr_done = 0;
+		return;
+	}
+	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+	    info->tty->hw_stopped) {
+		info->IER &= ~UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+		return;
+	}
+	
+	count = info->xmit_fifo_size;
+	do {
+		serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
+		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		if (--info->xmit_cnt <= 0)
+			break;
+	} while (--count > 0);
+	
+	if (info->xmit_cnt < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+	if (intr_done)
+		*intr_done = 0;
+
+	if (info->xmit_cnt <= 0) {
+		info->IER &= ~UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+}
+
+static _INLINE_ void check_modem_status(struct async_struct *info)
+{
+	int	status;
+	
+	status = serial_in(info, UART_MSR);
+
+	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttys%d CD now %s...", info->line,
+		       (status & UART_MSR_DCD) ? "on" : "off");
+#endif		
+		if (status & UART_MSR_DCD)
+			wake_up_interruptible(&info->open_wait);
+		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("scheduling hangup...");
+#endif
+			queue_task_irq_off(&info->tqueue_hangup,
+					   &tq_scheduler);
+		}
+	}
+	if (info->flags & ASYNC_CTS_FLOW) {
+		if (info->tty->hw_stopped) {
+			if (status & UART_MSR_CTS) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx start...");
+#endif
+				info->tty->hw_stopped = 0;
+				info->IER |= UART_IER_THRI;
+				serial_out(info, UART_IER, info->IER);
+				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				return;
+			}
+		} else {
+			if (!(status & UART_MSR_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx stop...");
+#endif
+				info->tty->hw_stopped = 1;
+				info->IER &= ~UART_IER_THRI;
+				serial_out(info, UART_IER, info->IER);
+			}
+		}
+	}
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void rs_interrupt(int irq, struct pt_regs * regs)
+{
+	int status;
+	struct async_struct * info;
+	int pass_counter = 0;
+	struct async_struct *end_mark = 0;
+	int first_multi = 0;
+	struct rs_multiport_struct *multi;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt(%d)...", irq);
+#endif
+	
+	info = IRQ_ports[irq];
+	if (!info)
+		return;
+	
+	multi = &rs_multiport[irq];
+	if (multi->port_monitor)
+		first_multi = inb(multi->port_monitor);
+
+	do {
+		if (!info->tty ||
+		    (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
+			if (!end_mark)
+				end_mark = info;
+			goto next;
+		}
+		end_mark = 0;
+
+		info->last_active = jiffies;
+
+		status = serial_inp(info, UART_LSR) & info->read_status_mask;
+#ifdef SERIAL_DEBUG_INTR
+		printk("status = %x...", status);
+#endif
+		if (status & UART_LSR_DR)
+			receive_chars(info, &status);
+		check_modem_status(info);
+		if (status & UART_LSR_THRE)
+			transmit_chars(info, 0);
+
+	next:
+		info = info->next_port;
+		if (!info) {
+			info = IRQ_ports[irq];
+			if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+#if 0
+				printk("rs loop break\n");
+#endif
+				break; 	/* Prevent infinite loops */
+			}
+			continue;
+		}
+	} while (end_mark != info);
+	if (multi->port_monitor)
+		printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
+		       info->irq, first_multi, inb(multi->port_monitor));
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq, struct pt_regs * regs)
+{
+	int status;
+	int pass_counter = 0;
+	int first_multi = 0;
+	struct async_struct * info;
+	struct rs_multiport_struct *multi;
+	
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+	
+	info = IRQ_ports[irq];
+	if (!info || !info->tty)
+		return;
+
+	multi = &rs_multiport[irq];
+	if (multi->port_monitor)
+		first_multi = inb(multi->port_monitor);
+
+	do {
+		status = serial_inp(info, UART_LSR) & info->read_status_mask;
+#ifdef SERIAL_DEBUG_INTR
+		printk("status = %x...", status);
+#endif
+		if (status & UART_LSR_DR)
+			receive_chars(info, &status);
+		check_modem_status(info);
+		if (status & UART_LSR_THRE)
+			transmit_chars(info, 0);
+		if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+#if 0
+			printk("rs_single loop break.\n");
+#endif
+			break;
+		}
+	} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
+	info->last_active = jiffies;
+	if (multi->port_monitor)
+		printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
+		       info->irq, first_multi, inb(multi->port_monitor));
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+/*
+ * This is the serial driver's for multiport boards
+ */
+static void rs_interrupt_multi(int irq, struct pt_regs * regs)
+{
+	int status;
+	struct async_struct * info;
+	int pass_counter = 0;
+	int first_multi= 0;
+	struct rs_multiport_struct *multi;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_multi(%d)...", irq);
+#endif
+	
+	info = IRQ_ports[irq];
+	if (!info)
+		return;
+	multi = &rs_multiport[irq];
+	if (!multi->port1) {
+		/* Should never happen */
+		printk("rs_interrupt_multi: NULL port1!\n");
+		return;
+	}
+	if (multi->port_monitor)
+		first_multi = inb(multi->port_monitor);
+	
+	while (1) {
+		if (!info->tty ||
+		    (serial_in(info, UART_IIR) & UART_IIR_NO_INT))
+			goto next;
+
+		info->last_active = jiffies;
+
+		status = serial_inp(info, UART_LSR) & info->read_status_mask;
+#ifdef SERIAL_DEBUG_INTR
+		printk("status = %x...", status);
+#endif
+		if (status & UART_LSR_DR)
+			receive_chars(info, &status);
+		check_modem_status(info);
+		if (status & UART_LSR_THRE)
+			transmit_chars(info, 0);
+
+	next:
+		info = info->next_port;
+		if (info)
+			continue;
+
+		info = IRQ_ports[irq];
+		if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+#if 1
+			printk("rs_multi loop break\n");
+#endif
+			break; 	/* Prevent infinite loops */
+		}
+		if (multi->port_monitor)
+			printk("rs port monitor irq %d: 0x%x, 0x%x\n",
+			       info->irq, first_multi,
+			       inb(multi->port_monitor));
+		if ((inb(multi->port1) & multi->mask1) != multi->match1)
+			continue;
+		if (!multi->port2)
+			break;
+		if ((inb(multi->port2) & multi->mask2) != multi->match2)
+			continue;
+		if (!multi->port3)
+			break;
+		if ((inb(multi->port3) & multi->mask3) != multi->match3)
+			continue;
+		if (!multi->port4)
+			break;
+		if ((inb(multi->port4) & multi->mask4) == multi->match4)
+			continue;
+		break;
+	} 
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void *unused)
+{
+	run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+	struct async_struct	*info = (struct async_struct *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred.  The path of
+ * hangup processing is:
+ *
+ * 	serial interrupt routine -> (scheduler tqueue) ->
+ * 	do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ * 
+ */
+static void do_serial_hangup(void *private_)
+{
+	struct async_struct	*info = (struct async_struct *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+}
+
+
+/*
+ * This subroutine is called when the RS_TIMER goes off.  It is used
+ * by the serial driver to handle ports that do not have an interrupt
+ * (irq=0).  This doesn't work very well for 16450's, but gives barely
+ * passable results for a 16550A.  (Although at the expense of much
+ * CPU overhead).
+ */
+static void rs_timer(void)
+{
+	static unsigned long last_strobe = 0;
+	struct async_struct *info;
+	unsigned int	i;
+
+	if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
+		for (i=1; i < 16; i++) {
+			info = IRQ_ports[i];
+			if (!info)
+				continue;
+			cli();
+			if (info->next_port) {
+				do {
+					serial_out(info, UART_IER, 0);
+					info->IER |= UART_IER_THRI;
+					serial_out(info, UART_IER, info->IER);
+					info = info->next_port;
+				} while (info);
+				if (rs_multiport[i].port1)
+					rs_interrupt_multi(i, NULL);
+				else
+					rs_interrupt(i, NULL);
+			} else
+				rs_interrupt_single(i, NULL);
+			sti();
+		}
+	}
+	last_strobe = jiffies;
+	timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME;
+	timer_active |= 1 << RS_TIMER;
+
+	if (IRQ_ports[0]) {
+		cli();
+		rs_interrupt(0, NULL);
+		sti();
+
+		timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
+	}
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver:  routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port.  Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+/*
+ * Grab all interrupts in preparation for doing an automatic irq
+ * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
+ * mask of irq's which were grabbed and should therefore be freed
+ * using free_all_interrupts().
+ */
+static int grab_all_interrupts(int dontgrab)
+{
+	int 			irq_lines = 0;
+	int			i, mask;
+	
+	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+		if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe")) {
+			irq_lines |= mask;
+		}
+	}
+	return irq_lines;
+}
+
+/*
+ * Release all interrupts grabbed by grab_all_interrupts
+ */
+static void free_all_interrupts(int irq_lines)
+{
+	int	i;
+	
+	for (i = 0; i < 16; i++) {
+		if (irq_lines & (1 << i))
+			free_irq(i);
+	}
+}
+
+/*
+ * This routine figures out the correct timeout for a particular IRQ.
+ * It uses the smallest timeout of all of the serial ports in a
+ * particular interrupt chain.  Now only used for IRQ 0....
+ */
+static void figure_IRQ_timeout(int irq)
+{
+	struct	async_struct	*info;
+	int	timeout = 60*HZ;	/* 60 seconds === a long time :-) */
+
+	info = IRQ_ports[irq];
+	if (!info) {
+		IRQ_timeout[irq] = 60*HZ;
+		return;
+	}
+	while (info) {
+		if (info->timeout < timeout)
+			timeout = info->timeout;
+		info = info->next_port;
+	}
+	if (!irq)
+		timeout = timeout / 2;
+	IRQ_timeout[irq] = timeout ? timeout : 1;
+}
+
+static int startup(struct async_struct * info)
+{
+	unsigned short ICP;
+	unsigned long flags;
+	int	retval;
+	void (*handler)(int, struct pt_regs *);
+
+	if (info->flags & ASYNC_INITIALIZED)
+		return 0;
+
+	if (!info->port || !info->type) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		return 0;
+	}
+
+	if (!info->xmit_buf) {
+		info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+		if (!info->xmit_buf)
+			return -ENOMEM;
+	}
+
+	save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttys%d (irq %d)...", info->line, info->irq);
+#endif
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	if (info->type == PORT_16650) {
+		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+					     UART_FCR_CLEAR_XMIT));
+		info->xmit_fifo_size = 1; /* disabled for now */
+	} else if (info->type == PORT_16550A) {
+		serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+					     UART_FCR_CLEAR_XMIT));
+		info->xmit_fifo_size = 16;
+	} else
+		info->xmit_fifo_size = 1;
+
+	/*
+	 * At this point there's no way the LSR could still be 0xFF;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (serial_inp(info, UART_LSR) == 0xff) {
+		restore_flags(flags);
+		if (suser()) {
+			if (info->tty)
+				set_bit(TTY_IO_ERROR, &info->tty->flags);
+			return 0;
+		} else
+			return -ENODEV;
+	}
+	
+	/*
+	 * Allocate the IRQ if necessary
+	 */
+	if (info->irq && (!IRQ_ports[info->irq] ||
+			  !IRQ_ports[info->irq]->next_port)) {
+		if (IRQ_ports[info->irq]) {
+			free_irq(info->irq);
+			if (rs_multiport[info->irq].port1)
+				handler = rs_interrupt_multi;
+			else
+				handler = rs_interrupt;
+		} else 
+			handler = rs_interrupt_single;
+
+		retval = request_irq(info->irq, handler, SA_INTERRUPT, "serial");
+		if (retval) {
+			restore_flags(flags);
+			if (suser()) {
+				if (info->tty)
+					set_bit(TTY_IO_ERROR,
+						&info->tty->flags);
+				return 0;
+			} else
+				return retval;
+		}
+	}
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+     /* (void) serial_inp(info, UART_LSR); */   /* (see above) */
+	(void) serial_inp(info, UART_RX);
+	(void) serial_inp(info, UART_IIR);
+	(void) serial_inp(info, UART_MSR);
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	serial_outp(info, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
+	if (info->flags & ASYNC_FOURPORT) {
+		info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+		info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
+	} else {
+		info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+		info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
+	}
+#if defined(__alpha__) && !defined (CONFIG_PCI)
+	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);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+	serial_outp(info, UART_IER, info->IER);	/* enable interrupts */
+	
+	if (info->flags & ASYNC_FOURPORT) {
+		/* Enable interrupts on the AST Fourport board */
+		ICP = (info->port & 0xFE0) | 0x01F;
+		outb_p(0x80, ICP);
+		(void) inb_p(ICP);
+	}
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void)serial_inp(info, UART_LSR);
+	(void)serial_inp(info, UART_RX);
+	(void)serial_inp(info, UART_IIR);
+	(void)serial_inp(info, UART_MSR);
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	/*
+	 * Insert serial port into IRQ chain.
+	 */
+	info->prev_port = 0;
+	info->next_port = IRQ_ports[info->irq];
+	if (info->next_port)
+		info->next_port->prev_port = info;
+	IRQ_ports[info->irq] = info;
+	figure_IRQ_timeout(info->irq);
+
+	/*
+	 * Set up serial timers...
+	 */
+	timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
+	timer_active |= 1 << RS_TIMER;
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(info);
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+	unsigned long	flags;
+	int		retval;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       info->irq);
+#endif
+	
+	save_flags(flags); cli(); /* Disable interrupts */
+	
+	/*
+	 * First unlink the serial port from the IRQ chain...
+	 */
+	if (info->next_port)
+		info->next_port->prev_port = info->prev_port;
+	if (info->prev_port)
+		info->prev_port->next_port = info->next_port;
+	else
+		IRQ_ports[info->irq] = info->next_port;
+	figure_IRQ_timeout(info->irq);
+	
+	/*
+	 * Free the IRQ, if necessary
+	 */
+	if (info->irq && (!IRQ_ports[info->irq] ||
+			  !IRQ_ports[info->irq]->next_port)) {
+		if (IRQ_ports[info->irq]) {
+			free_irq(info->irq);
+			retval = request_irq(info->irq, rs_interrupt_single, SA_INTERRUPT, "serial");
+			
+			if (retval)
+				printk("serial shutdown: request_irq: error %d"
+				       "  Couldn't reacquire IRQ.\n", retval);
+		} else
+			free_irq(info->irq);
+	}
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	info->IER = 0;
+	serial_outp(info, UART_IER, 0x00);	/* disable all intrs */
+	if (info->flags & ASYNC_FOURPORT) {
+		/* reset interrupts on the AST Fourport board */
+		(void) inb((info->port & 0xFE0) | 0x01F);
+	}
+	
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+		info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);
+	}
+	serial_outp(info, UART_MCR, info->MCR_noint);
+
+	/* disable FIFO's */	
+	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+				     UART_FCR_CLEAR_XMIT));
+	(void)serial_in(info, UART_RX);    /* read data port to reset things */
+	
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+	
+	info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct async_struct *info)
+{
+	unsigned short port;
+	int	quot = 0;
+	unsigned cflag,cval,fcr;
+	int	i;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+	if (!(port = info->port))
+		return;
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 2) 
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (i == 15) {
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			i += 1;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			i += 2;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+			quot = info->custom_divisor;
+	}
+	if (quot) {
+		info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
+				 info->baud_base) + 2;
+	} else if (baud_table[i] == 134) {
+		quot = (2*info->baud_base / 269);
+		info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+	} else if (baud_table[i]) {
+		quot = info->baud_base / baud_table[i];
+		info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
+	} else {
+		quot = 0;
+		info->timeout = 0;
+	}
+	if (quot) {
+		info->MCR |= UART_MCR_DTR;
+		info->MCR_noint |= UART_MCR_DTR;
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
+		sti();
+	} else {
+		info->MCR &= ~UART_MCR_DTR;
+		info->MCR_noint &= ~UART_MCR_DTR;
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
+		sti();
+		return;
+	}
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+		case CS5: cval = 0x00; break;
+		case CS6: cval = 0x01; break;
+		case CS7: cval = 0x02; break;
+		case CS8: cval = 0x03; break;
+		default: cval = 0x00; break;   /* too keep GCC shut... */
+	}
+	if (cflag & CSTOPB) {
+		cval |= 0x04;
+	}	
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+	if (info->type == PORT_16550A) {
+		if ((info->baud_base / quot) < 2400)
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+		else
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+	} else if (info->type == PORT_16650) {
+		/*
+		 * On the 16650, we disable the FIFOs altogether
+		 * because of a design bug in how the implement
+		 * things.  We could support it by completely changing
+		 * how we handle the interrupt driver, but not today....
+		 *
+		 * N.B.  Because there's no way to set a FIFO trigger
+		 * at 1 char, we'd probably disable at speed below
+		 * 2400 baud anyway...
+		 */
+		fcr = 0;
+	} else
+		fcr = 0;
+	
+	/* CTS flow control flag and modem status interrupts */
+	info->IER &= ~UART_IER_MSI;
+	if (cflag & CRTSCTS) {
+		info->flags |= ASYNC_CTS_FLOW;
+		info->IER |= UART_IER_MSI;
+	} else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else {
+		info->flags |= ASYNC_CHECK_CD;
+		info->IER |= UART_IER_MSI;
+	}
+	serial_out(info, UART_IER, info->IER);
+
+	/*
+	 * Set up parity check flag
+	 */
+	info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (I_INPCK(info->tty))
+		info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+		info->read_status_mask |= UART_LSR_BI;
+	
+	info->ignore_status_mask = 0;
+	if (I_IGNPAR(info->tty)) {
+		info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+		info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	}
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_status_mask |= UART_LSR_BI;
+		info->read_status_mask |= UART_LSR_BI;
+		/*
+		 * If we're ignore parity and break indicators, ignore 
+		 * overruns too.  (For real raw support).
+		 */
+		if (I_IGNPAR(info->tty)) {
+			info->ignore_status_mask |= UART_LSR_OE;
+			info->read_status_mask |= UART_LSR_OE;
+		}
+	}
+	
+	cli();
+	serial_outp(info, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	serial_outp(info, UART_DLL, quot & 0xff);	/* LS of divisor */
+	serial_outp(info, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_outp(info, UART_LCR, cval);		/* reset DLAB */
+	serial_outp(info, UART_FCR, fcr); 	/* set fcr */
+	sti();
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+		return;
+
+	if (!tty || !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	info->xmit_buf[info->xmit_head++] = ch;
+	info->xmit_head &= SERIAL_XMIT_SIZE-1;
+	info->xmit_cnt++;
+	restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	info->IER |= UART_IER_THRI;
+	serial_out(info, UART_IER, info->IER);
+	restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, total = 0;
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf || !tmp_buf)
+		return 0;
+	    
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - info->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			down(&tmp_buf_sem);
+			memcpy_fromfs(tmp_buf, buf, c);
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				       SERIAL_XMIT_SIZE - info->xmit_head));
+			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+			up(&tmp_buf_sem);
+		} else
+			memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    !(info->IER & UART_IER_THRI)) {
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_IER, info->IER);
+	}
+	restore_flags(flags);
+	return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	int	ret;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+		return 0;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+		return;
+	cli();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	sti();
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+		return;
+	
+	if (I_IXOFF(tty))
+		info->x_char = STOP_CHAR(tty);
+
+	info->MCR &= ~UART_MCR_RTS;
+	info->MCR_noint &= ~UART_MCR_RTS;
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+		return;
+	
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			info->x_char = START_CHAR(tty);
+	}
+	info->MCR |= UART_MCR_RTS;
+	info->MCR_noint |= UART_MCR_RTS;
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct async_struct * info,
+			   struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+  
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = info->port;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	tmp.hub6 = info->hub6;
+	memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+	return 0;
+}
+
+static int set_serial_info(struct async_struct * info,
+			   struct serial_struct * new_info)
+{
+	struct serial_struct new_serial;
+	struct async_struct old_info;
+	unsigned int		i,change_irq,change_port;
+	int 			retval = 0;
+
+	if (!new_info)
+		return -EFAULT;
+	memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+	old_info = *info;
+
+	change_irq = new_serial.irq != info->irq;
+	change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
+
+	if (!suser()) {
+		if (change_irq || change_port ||
+		    (new_serial.baud_base != info->baud_base) ||
+		    (new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
+		     (info->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+			       (new_serial.flags & ASYNC_USR_MASK));
+		info->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (new_serial.irq == 2)
+		new_serial.irq = 9;
+
+	if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||
+	    (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {
+		return -EINVAL;
+	}
+
+	/* Make sure address is not already in use */
+	if (new_serial.type) {
+		for (i = 0 ; i < NR_PORTS; i++)
+			if ((info != &rs_table[i]) &&
+			    (rs_table[i].port == new_serial.port) &&
+			    rs_table[i].type)
+				return -EADDRINUSE;
+	}
+
+	if ((change_port || change_irq) && (info->count > 1))
+		return -EBUSY;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ASYNC_FLAGS) |
+			(new_serial.flags & ASYNC_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay * HZ/100;
+	info->closing_wait = new_serial.closing_wait * HZ/100;
+
+	release_region(info->port,8);
+	if (change_port || change_irq) {
+		/*
+		 * We need to shutdown the serial port at the old
+		 * port/irq combination.
+		 */
+		shutdown(info);
+		info->irq = new_serial.irq;
+		info->port = new_serial.port;
+		info->hub6 = new_serial.hub6;
+	}
+	if(info->type != PORT_UNKNOWN)
+		request_region(info->port,8,"serial(set)");
+
+	
+check_and_exit:
+	if (!info->port || !info->type)
+		return 0;
+	if (info->flags & ASYNC_INITIALIZED) {
+		if (((old_info.flags & ASYNC_SPD_MASK) !=
+		     (info->flags & ASYNC_SPD_MASK)) ||
+		    (old_info.custom_divisor != info->custom_divisor))
+			change_speed(info);
+	} else
+		retval = startup(info);
+	return retval;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct async_struct * info, unsigned int *value)
+{
+	unsigned char status;
+	unsigned int result;
+
+	cli();
+	status = serial_in(info, UART_LSR);
+	sti();
+	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+	put_user(result,value);
+	return 0;
+}
+
+
+static int get_modem_info(struct async_struct * info, unsigned int *value)
+{
+	unsigned char control, status;
+	unsigned int result;
+
+	control = info->MCR;
+	cli();
+	status = serial_in(info, UART_MSR);
+	sti();
+	result =  ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+		| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+		| ((status  & UART_MSR_DCD) ? TIOCM_CAR : 0)
+		| ((status  & UART_MSR_RI) ? TIOCM_RNG : 0)
+		| ((status  & UART_MSR_DSR) ? TIOCM_DSR : 0)
+		| ((status  & UART_MSR_CTS) ? TIOCM_CTS : 0);
+	put_user(result,value);
+	return 0;
+}
+
+static int set_modem_info(struct async_struct * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+
+	error = verify_area(VERIFY_READ, value, sizeof(int));
+	if (error)
+		return error;
+	arg = get_user(value);
+	switch (cmd) {
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS) {
+			info->MCR |= UART_MCR_RTS;
+			info->MCR_noint |= UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR |= UART_MCR_DTR;
+			info->MCR_noint |= UART_MCR_DTR;
+		}
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) {
+			info->MCR &= ~UART_MCR_RTS;
+			info->MCR_noint &= ~UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR &= ~UART_MCR_DTR;
+			info->MCR_noint &= ~UART_MCR_DTR;
+		}
+		break;
+	case TIOCMSET:
+		info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		info->MCR_noint = ((info->MCR_noint
+				    & ~(UART_MCR_RTS | UART_MCR_DTR))
+				   | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+				   | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		break;
+	default:
+		return -EINVAL;
+	}
+	cli();
+	serial_out(info, UART_MCR, info->MCR);
+	sti();
+	return 0;
+}
+
+static int do_autoconfig(struct async_struct * info)
+{
+	int			retval;
+	
+	if (!suser())
+		return -EPERM;
+	
+	if (info->count > 1)
+		return -EBUSY;
+	
+	shutdown(info);
+
+	cli();
+	autoconfig(info);
+	sti();
+
+	retval = startup(info);
+	if (retval)
+		return retval;
+	return 0;
+}
+
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(	struct async_struct * info, int duration)
+{
+	if (!info->port)
+		return;
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+	cli();
+	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
+	schedule();
+	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+	sti();
+}
+
+/*
+ * This routine returns a bitfield of "wild interrupts".  Basically,
+ * any unclaimed interrupts which is flapping around.
+ */
+static int check_wild_interrupts(int doprint)
+{
+	int	i, mask;
+	int	wild_interrupts = 0;
+	int	irq_lines;
+	unsigned long timeout;
+	unsigned long flags;
+	
+	/* Turn on interrupts (they may be off) */
+	save_flags(flags); sti();
+
+	irq_lines = grab_all_interrupts(0);
+	
+	/*
+	 * Delay for 0.1 seconds -- we use a busy loop since this may 
+	 * occur during the bootup sequence
+	 */
+	timeout = jiffies+HZ/10;
+	while (timeout >= jiffies)
+		;
+	
+	rs_triggered = 0;	/* Reset after letting things settle */
+
+	timeout = jiffies+HZ/10;
+	while (timeout >= jiffies)
+		;
+	
+	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+		if ((rs_triggered & (1 << i)) &&
+		    (irq_lines & (1 << i))) {
+			wild_interrupts |= mask;
+			if (doprint)
+				printk("Wild interrupt?  (IRQ %d)\n", i);
+		}
+	}
+	free_all_interrupts(irq_lines);
+	restore_flags(flags);
+	return wild_interrupts;
+}
+
+static int get_multiport_struct(struct async_struct * info,
+				struct serial_multiport_struct *retinfo)
+{
+	struct serial_multiport_struct ret;
+	struct rs_multiport_struct *multi;
+	
+	multi = &rs_multiport[info->irq];
+
+	ret.port_monitor = multi->port_monitor;
+	
+	ret.port1 = multi->port1;
+	ret.mask1 = multi->mask1;
+	ret.match1 = multi->match1;
+	
+	ret.port2 = multi->port2;
+	ret.mask2 = multi->mask2;
+	ret.match2 = multi->match2;
+	
+	ret.port3 = multi->port3;
+	ret.mask3 = multi->mask3;
+	ret.match3 = multi->match3;
+	
+	ret.port4 = multi->port4;
+	ret.mask4 = multi->mask4;
+	ret.match4 = multi->match4;
+
+	ret.irq = info->irq;
+
+	memcpy_tofs(retinfo,&ret,sizeof(*retinfo));
+	return 0;
+	
+}
+
+static int set_multiport_struct(struct async_struct * info,
+				struct serial_multiport_struct *in_multi)
+{
+	struct serial_multiport_struct new_multi;
+	struct rs_multiport_struct *multi;
+	int	was_multi, now_multi;
+	int	retval;
+	void (*handler)(int, struct pt_regs *);
+
+	if (!suser())
+		return -EPERM;
+	if (!in_multi)
+		return -EFAULT;
+	memcpy_fromfs(&new_multi, in_multi,
+		      sizeof(struct serial_multiport_struct));
+
+	if (new_multi.irq != info->irq || info->irq == 0 ||
+	    !IRQ_ports[info->irq])
+		return -EINVAL;
+
+	multi = &rs_multiport[info->irq];
+	was_multi = (multi->port1 != 0);
+	
+	multi->port_monitor = new_multi.port_monitor;
+	
+	if (multi->port1)
+		release_region(multi->port1,1);
+	multi->port1 = new_multi.port1;
+	multi->mask1 = new_multi.mask1;
+	multi->match1 = new_multi.match1;
+	if (multi->port1)
+		request_region(multi->port1,1,"serial(multiport1)");
+
+	if (multi->port2)
+		release_region(multi->port2,1);
+	multi->port2 = new_multi.port2;
+	multi->mask2 = new_multi.mask2;
+	multi->match2 = new_multi.match2;
+	if (multi->port2)
+		request_region(multi->port2,1,"serial(multiport2)");
+
+	if (multi->port3)
+		release_region(multi->port3,1);
+	multi->port3 = new_multi.port3;
+	multi->mask3 = new_multi.mask3;
+	multi->match3 = new_multi.match3;
+	if (multi->port3)
+		request_region(multi->port3,1,"serial(multiport3)");
+
+	if (multi->port4)
+		release_region(multi->port4,1);
+	multi->port4 = new_multi.port4;
+	multi->mask4 = new_multi.mask4;
+	multi->match4 = new_multi.match4;
+	if (multi->port4)
+		request_region(multi->port4,1,"serial(multiport4)");
+
+	now_multi = (multi->port1 != 0);
+	
+	if (IRQ_ports[info->irq]->next_port &&
+	    (was_multi != now_multi)) {
+		free_irq(info->irq);
+		if (now_multi)
+			handler = rs_interrupt_multi;
+		else
+			handler = rs_interrupt;
+
+		retval = request_irq(info->irq, handler, SA_INTERRUPT,
+				     "serial");
+		if (retval) {
+			printk("Couldn't reallocate serial interrupt "
+			       "driver!!\n");
+		}
+	}
+
+	return 0;
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error;
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	int retval;
+
+	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+	
+	switch (cmd) {
+		case TCSBRK:	/* SVID version: non-zero arg --> no break */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (!arg)
+				send_break(info, HZ/4);	/* 1/4 second */
+			return 0;
+		case TCSBRKP:	/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			send_break(info, arg ? arg*(HZ/10) : HZ/4);
+			return 0;
+		case TIOCGSOFTCAR:
+			error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
+			if (error)
+				return error;
+			put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+				    (unsigned long *) arg);
+			return 0;
+		case TIOCSSOFTCAR:
+			arg = get_fs_long((unsigned long *) arg);
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+				sizeof(unsigned int));
+			if (error)
+				return error;
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case TIOCGSERIAL:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+						sizeof(struct serial_struct));
+			if (error)
+				return error;
+			return get_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSERCONFIG:
+			return do_autoconfig(info);
+
+		case TIOCSERGWILD:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+					    sizeof(int));
+			if (error)
+				return error;
+			put_fs_long(rs_wild_int_mask, (unsigned long *) arg);
+			return 0;
+
+		case TIOCSERGETLSR: /* Get line status register */
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+				sizeof(unsigned int));
+			if (error)
+				return error;
+			else
+			    return get_lsr_info(info, (unsigned int *) arg);
+
+		case TIOCSERSWILD:
+			if (!suser())
+				return -EPERM;
+			rs_wild_int_mask = get_fs_long((unsigned long *) arg);
+			if (rs_wild_int_mask < 0)
+				rs_wild_int_mask = check_wild_interrupts(0);
+			return 0;
+
+		case TIOCSERGSTRUCT:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+						sizeof(struct async_struct));
+			if (error)
+				return error;
+			memcpy_tofs((struct async_struct *) arg,
+				    info, sizeof(struct async_struct));
+			return 0;
+			
+		case TIOCSERGETMULTI:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+				    sizeof(struct serial_multiport_struct));
+			if (error)
+				return error;
+			return get_multiport_struct(info,
+				       (struct serial_multiport_struct *) arg);
+		case TIOCSERSETMULTI:
+			return set_multiport_struct(info,
+				       (struct serial_multiport_struct *) arg);
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag)
+		return;
+
+	change_speed(info);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+
+#if 0
+	/*
+	 * No need to wake up processes in open wait, since they
+	 * sample the CLOCAL flag once, and don't recheck it.
+	 * XXX  It's not clear whether the current behavior is correct
+	 * or not.  Hence, this may change.....
+	 */
+	if (!(old_termios->c_cflag & CLOCAL) &&
+	    (tty->termios->c_cflag & CLOCAL))
+		wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ * 
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+	unsigned long timeout;
+
+	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+		return;
+	
+	save_flags(flags); cli();
+	
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	info->IER &= ~UART_IER_RLSI;
+	info->read_status_mask &= ~UART_LSR_DR;
+	if (info->flags & ASYNC_INITIALIZED) {
+		serial_out(info, UART_IER, info->IER);
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies+HZ;
+		while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->timeout;
+			schedule();
+			if (jiffies > timeout)
+				break;
+		}
+	}
+	shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+	if (tty->ldisc.num != ldiscs[N_TTY].num) {
+		if (tty->ldisc.close)
+			(tty->ldisc.close)(tty);
+		tty->ldisc = ldiscs[N_TTY];
+		tty->termios->c_line = N_TTY;
+		if (tty->ldisc.open)
+			(tty->ldisc.open)(tty);
+	}
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	restore_flags(flags);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+		return;
+	
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct async_struct *info)
+{
+	struct wait_queue wait = { current, NULL };
+	int		retval;
+	int		do_clocal = 0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (info->flags & ASYNC_CLOSING) {
+		interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		if (info->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	info->count--;
+	info->blocked_open++;
+	while (1) {
+		cli();
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
+			serial_out(info, UART_MCR,
+				   serial_inp(info, UART_MCR) |
+				   (UART_MCR_DTR | UART_MCR_RTS));
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(info->flags & ASYNC_CLOSING) &&
+		    (do_clocal || (serial_in(info, UART_MSR) &
+				   UART_MSR_DCD)))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttys%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}	
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct async_struct	*info;
+	int 			retval, line;
+
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS))
+		return -ENODEV;
+	info = rs_table + line;
+	if (serial_paranoia_check(info, tty->device, "rs_open"))
+		return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->count);
+#endif
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	if (!tmp_buf) {
+		tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+		if (!tmp_buf)
+			return -ENOMEM;
+	}
+	
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("rs_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else 
+			*tty->termios = info->callout_termios;
+		change_speed(info);
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open ttys%d successful...", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void show_serial_version(void)
+{
+	printk("Serial driver version 4.11 with");
+#ifdef CONFIG_HUB6
+	printk(" HUB-6");
+#define SERIAL_OPT
+#endif
+#ifdef SERIAL_OPT
+	printk(" enabled\n");
+#else
+	printk(" no serial options enabled\n");
+#endif
+#undef SERIAL_OPT
+}
+
+/*
+ * This routine is called by do_auto_irq(); it attempts to determine
+ * which interrupt a serial port is configured to use.  It is not
+ * fool-proof, but it works a large part of the time.
+ */
+static int get_auto_irq(struct async_struct *info)
+{
+	unsigned char save_MCR, save_IER, save_ICP=0;
+	unsigned short ICP=0, port = info->port;
+	unsigned long timeout;
+	
+	/*
+	 * Enable interrupts and see who answers
+	 */
+	rs_irq_triggered = 0;
+	cli();
+	save_IER = serial_inp(info, UART_IER);
+	save_MCR = serial_inp(info, UART_MCR);
+	if (info->flags & ASYNC_FOURPORT)  {
+		serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
+		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
+		ICP = (port & 0xFE0) | 0x01F;
+		save_ICP = inb_p(ICP);
+		outb_p(0x80, ICP);
+		(void) inb_p(ICP);
+	} else {
+		serial_outp(info, UART_MCR,
+			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
+	}
+	sti();
+	/*
+	 * Next, clear the interrupt registers.
+	 */
+	(void)serial_inp(info, UART_LSR);
+	(void)serial_inp(info, UART_RX);
+	(void)serial_inp(info, UART_IIR);
+	(void)serial_inp(info, UART_MSR);
+	
+	timeout = jiffies+2*HZ/100;
+	while (timeout >= jiffies) {
+		if (rs_irq_triggered)
+			break;
+	}
+	/*
+	 * Now check to see if we got any business, and clean up.
+	 */
+	cli();
+	serial_outp(info, UART_IER, save_IER);
+	serial_outp(info, UART_MCR, save_MCR);
+	if (info->flags & ASYNC_FOURPORT)
+		outb_p(save_ICP, ICP);
+	sti();
+	return(rs_irq_triggered);
+}
+
+/*
+ * Calls get_auto_irq() multiple times, to make sure we don't get
+ * faked out by random interrupts
+ */
+static int do_auto_irq(struct async_struct * info)
+{
+	unsigned 		port = info->port;
+	int 			irq_lines = 0;
+	int			irq_try_1 = 0, irq_try_2 = 0;
+	int			retries;
+	unsigned long flags;
+
+	if (!port)
+		return 0;
+
+	/* Turn on interrupts (they may be off) */
+	save_flags(flags); sti();
+
+	irq_lines = grab_all_interrupts(rs_wild_int_mask);
+	
+	for (retries = 0; retries < 5; retries++) {
+		if (!irq_try_1)
+			irq_try_1 = get_auto_irq(info);
+		if (!irq_try_2)
+			irq_try_2 = get_auto_irq(info);
+		if (irq_try_1 && irq_try_2) {
+			if (irq_try_1 == irq_try_2)
+				break;
+			irq_try_1 = irq_try_2 = 0;
+		}
+	}
+	restore_flags(flags);
+	free_all_interrupts(irq_lines);
+	return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.  It determines what type of UART ship this serial port is
+ * using: 8250, 16450, 16550, 16550A.  The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void autoconfig(struct async_struct * info)
+{
+	unsigned char status1, status2, scratch, scratch2;
+	unsigned port = info->port;
+	unsigned long flags;
+
+	info->type = PORT_UNKNOWN;
+	
+	if (!port)
+		return;
+
+	save_flags(flags); cli();
+	
+	/*
+	 * Do a simple existence test first; if we fail this, there's
+	 * no point trying anything else.
+	 *
+	 * 0x80 is used as a nonsense port to prevent against false
+	 * positives due to ISA bus float.  The assumption is that
+	 * 0x80 is a non-existent port; which should be safe since
+	 * include/asm/io.h also makes this assumption.
+	 */
+	scratch = serial_inp(info, UART_IER);
+	serial_outp(info, UART_IER, 0);
+	outb(0xff, 0x080);
+	scratch2 = serial_inp(info, UART_IER);
+	serial_outp(info, UART_IER, scratch);
+	if (scratch2) {
+		restore_flags(flags);
+		return;		/* We failed; there's nothing here */
+	}
+
+	/* 
+	 * Check to see if a UART is really there.  Certain broken
+	 * internal modems based on the Rockwell chipset fail this
+	 * test, because they apparently don't implement the loopback
+	 * test mode.  So this test is skipped on the COM 1 through
+	 * COM 4 ports.  This *should* be safe, since no board
+	 * manufacturer would be stupid enough to design a board
+	 * that conflicts with COM 1-4 --- we hope!
+	 */
+	if (!(info->flags & ASYNC_SKIP_TEST)) {
+		scratch = serial_inp(info, UART_MCR);
+		serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
+		scratch2 = serial_inp(info, UART_MSR);
+		serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
+		status1 = serial_inp(info, UART_MSR) & 0xF0;
+		serial_outp(info, UART_MCR, scratch);
+		serial_outp(info, UART_MSR, scratch2);
+		if (status1 != 0x90) {
+			restore_flags(flags);
+			return;
+		}
+	} 
+	
+	/*
+	 * If the AUTO_IRQ flag is set, try to do the automatic IRQ
+	 * detection.
+	 */
+	if (info->flags & ASYNC_AUTO_IRQ)
+		info->irq = do_auto_irq(info);
+		
+	scratch2 = serial_in(info, UART_LCR);
+	serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+	serial_outp(info, UART_EFR, 0);	/* EFR is the same as FCR */
+	serial_outp(info, UART_LCR, scratch2);
+	serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+	scratch = serial_in(info, UART_IIR) >> 6;
+	info->xmit_fifo_size = 1;
+	switch (scratch) {
+		case 0:
+			info->type = PORT_16450;
+			break;
+		case 1:
+			info->type = PORT_UNKNOWN;
+			break;
+		case 2:
+			info->type = PORT_16550;
+			break;
+		case 3:
+			serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+			if (serial_in(info, UART_EFR) == 0) {
+				info->type = PORT_16650;
+				info->xmit_fifo_size = 32;
+			} else {
+				info->type = PORT_16550A;
+				info->xmit_fifo_size = 16;
+			}
+			serial_outp(info, UART_LCR, scratch2);
+			break;
+	}
+	if (info->type == PORT_16450) {
+		scratch = serial_in(info, UART_SCR);
+		serial_outp(info, UART_SCR, 0xa5);
+		status1 = serial_in(info, UART_SCR);
+		serial_outp(info, UART_SCR, 0x5a);
+		status2 = serial_in(info, UART_SCR);
+		serial_outp(info, UART_SCR, scratch);
+
+		if ((status1 != 0xa5) || (status2 != 0x5a))
+			info->type = PORT_8250;
+	}
+	request_region(info->port,8,"serial(auto)");
+
+	/*
+	 * Reset the UART.
+	 */
+#if defined(__alpha__) && !defined(CONFIG_PCI)
+	/*
+	 * 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);
+	
+	restore_flags(flags);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int rs_init(void)
+{
+	int i;
+	struct async_struct * info;
+	
+	bh_base[SERIAL_BH].routine = do_serial_bh;
+	enable_bh(SERIAL_BH);
+	timer_table[RS_TIMER].fn = rs_timer;
+	timer_table[RS_TIMER].expires = 0;
+#ifdef CONFIG_AUTO_IRQ
+	rs_wild_int_mask = check_wild_interrupts(1);
+#endif
+
+	for (i = 0; i < 16; i++) {
+		IRQ_ports[i] = 0;
+		IRQ_timeout[i] = 0;
+		memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
+	}
+	
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = NR_PORTS;
+	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver.subtype = SERIAL_TYPE_NORMAL;
+	serial_driver.init_termios = tty_std_termios;
+	serial_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	serial_driver.refcount = &serial_refcount;
+	serial_driver.table = serial_table;
+	serial_driver.termios = serial_termios;
+	serial_driver.termios_locked = serial_termios_locked;
+
+	serial_driver.open = rs_open;
+	serial_driver.close = rs_close;
+	serial_driver.write = rs_write;
+	serial_driver.put_char = rs_put_char;
+	serial_driver.flush_chars = rs_flush_chars;
+	serial_driver.write_room = rs_write_room;
+	serial_driver.chars_in_buffer = rs_chars_in_buffer;
+	serial_driver.flush_buffer = rs_flush_buffer;
+	serial_driver.ioctl = rs_ioctl;
+	serial_driver.throttle = rs_throttle;
+	serial_driver.unthrottle = rs_unthrottle;
+	serial_driver.set_termios = rs_set_termios;
+	serial_driver.stop = rs_stop;
+	serial_driver.start = rs_start;
+	serial_driver.hangup = rs_hangup;
+
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+	if (tty_register_driver(&serial_driver))
+		panic("Couldn't register serial driver\n");
+	if (tty_register_driver(&callout_driver))
+		panic("Couldn't register callout driver\n");
+	
+	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+		info->magic = SERIAL_MAGIC;
+		info->line = i;
+		info->tty = 0;
+		info->type = PORT_UNKNOWN;
+		info->custom_divisor = 0;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		info->tqueue_hangup.routine = do_serial_hangup;
+		info->tqueue_hangup.data = info;
+		info->callout_termios =callout_driver.init_termios;
+		info->normal_termios = serial_driver.init_termios;
+		info->open_wait = 0;
+		info->close_wait = 0;
+		info->next_port = 0;
+		info->prev_port = 0;
+		if (info->irq == 2)
+			info->irq = 9;
+		if (!(info->flags & ASYNC_BOOT_AUTOCONF))
+			continue;
+		autoconfig(info);
+		if (info->type == PORT_UNKNOWN)
+			continue;
+		printk("tty%02d%s at 0x%04x (irq = %d)", info->line, 
+		       (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
+		       info->port, info->irq);
+		switch (info->type) {
+			case PORT_8250:
+				printk(" is a 8250\n");
+				break;
+			case PORT_16450:
+				printk(" is a 16450\n");
+				break;
+			case PORT_16550:
+				printk(" is a 16550\n");
+				break;
+			case PORT_16550A:
+				printk(" is a 16550A\n");
+				break;
+			case PORT_16650:
+				printk(" is a 16650\n");
+				break;
+			default:
+				printk("\n");
+				break;
+		}
+	}
+	return 0;
+}
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+int register_serial(struct serial_struct *req)
+{
+	int i;
+	unsigned long flags;
+	struct async_struct *info;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < NR_PORTS; i++) {
+		if (rs_table[i].port == req->port)
+			break;
+	}
+	if (i == NR_PORTS) {
+		for (i = 0; i < NR_PORTS; i++)
+			if ((rs_table[i].type == PORT_UNKNOWN) &&
+			    (rs_table[i].count == 0))
+				break;
+	}
+	if (i == NR_PORTS) {
+		restore_flags(flags);
+		return -1;
+	}
+	info = &rs_table[i];
+	if (rs_table[i].count) {
+		restore_flags(flags);
+		printk("Couldn't configure serial #%d (port=%d,irq=%d): "
+		       "device already open\n", i, req->port, req->irq);
+		return -1;
+	}
+	info->irq = req->irq;
+	info->port = req->port;
+	autoconfig(info);
+	if (info->type == PORT_UNKNOWN) {
+		restore_flags(flags);
+		printk("register_serial(): autoconfig failed\n");
+		return -1;
+	}
+	printk("tty%02d at 0x%04x (irq = %d)", info->line, 
+	       info->port, info->irq);
+	switch (info->type) {
+	case PORT_8250:
+		printk(" is a 8250\n"); break;
+	case PORT_16450:
+		printk(" is a 16450\n"); break;
+	case PORT_16550:
+		printk(" is a 16550\n"); break;
+	case PORT_16550A:
+		printk(" is a 16550A\n"); break;
+	default:
+		printk("\n"); break;
+	}
+	restore_flags(flags);
+	return info->line;
+}
+
+void unregister_serial(int line)
+{
+	unsigned long flags;
+	struct async_struct *info = &rs_table[line];
+
+	save_flags(flags);
+	cli();
+	if (info->tty)
+		tty_hangup(info->tty);
+	info->type = PORT_UNKNOWN;
+	printk("tty%02d unloaded\n", info->line);
+	restore_flags(flags);
+}
diff -urNwbB linux/arch/arm/drivers/char/tty_io.c linux.arm/arch/arm/drivers/char/tty_io.c
--- linux/arch/arm/drivers/char/tty_io.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/tty_io.c	Sun Mar  3 12:48:37 1996
@@ -0,0 +1,1799 @@
+/*
+ *  linux/arch/arm/drivers/char/tty_io.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Modifications for ARM processor Copright (C) 1995, 1996 Russell King.
+ */
+
+/*
+ * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
+ * or rs-channels. It also implements echoing, cooked mode etc.
+ *
+ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
+ *
+ * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
+ * tty_struct and tty_queue structures.  Previously there was a array
+ * of 256 tty_struct's which was statically allocated, and the
+ * tty_queue structures were allocated at boot time.  Both are now
+ * dynamically allocated only when the tty is open.
+ *
+ * Also restructured routines so that there is more of a separation
+ * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
+ * the low-level tty routines (serial.c, pty.c, console.c).  This
+ * makes for cleaner and more compact code.  -TYT, 9/17/92 
+ *
+ * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
+ * which can be dynamically activated and de-activated by the line
+ * discipline handling modules (like SLIP).
+ *
+ * NOTE: pay no attention to the line discipline code (yet); its
+ * interface is still subject to change in this version...
+ * -- TYT, 1/31/92
+ *
+ * Added functionality to the OPOST tty handling.  No delays, but all
+ * other bits should be there.
+ *	-- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
+ *
+ * Rewrote canonical mode and added more termios flags.
+ * 	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
+ *
+ * Reorganized FASYNC support so mouse code can share it.
+ *	-- ctm@ardi.com, 9Sep 95
+ *
+ * Altered get_free_page to kmalloc's for tty structures to save memory
+ *  on the ARM processor
+ *      -- rmk92@ecs.soton.ac.uk
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+/*#include <linux/scc.h>*/
+
+#include "kbd_kern.h"
+#include "vt_kern.h"
+#include "selection.h"
+
+#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
+
+#undef TTY_DEBUG_HANGUP
+
+#define TTY_PARANOIA_CHECK
+#define CHECK_TTY_COUNT
+
+extern void do_blank_screen(int nopowersave);
+/*extern void set_vesa_blanking(const unsigned long arg);*/
+
+struct termios tty_std_termios;		/* for the benefit of tty drivers  */
+struct tty_driver *tty_drivers = NULL;	/* linked list of tty drivers */
+struct tty_ldisc ldiscs[NR_LDISCS];	/* line disc dispatch table	*/
+
+/*
+ * fg_console is the current virtual console,
+ * last_console is the last used one
+ * redirect is the pseudo-tty that console output
+ * is redirected to if asked by TIOCCONS.
+ */
+int fg_console = 0;
+int last_console = 0;
+struct tty_struct * redirect = NULL;
+struct wait_queue * keypress_wait = NULL;
+
+static void initialize_tty_struct(struct tty_struct *tty);
+
+static int tty_read(struct inode *, struct file *, char *, int);
+static int tty_write(struct inode *, struct file *, const char *, int);
+static int tty_select(struct inode *, struct file *, int, select_table *);
+static int tty_open(struct inode *, struct file *);
+static void tty_release(struct inode *, struct file *);
+static int tty_ioctl(struct inode * inode, struct file * file,
+		     unsigned int cmd, unsigned long arg);
+static int tty_fasync(struct inode * inode, struct file * filp, int on);
+
+extern void reset_palette(int currcons) ;
+extern void set_palette(void) ;
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * These two routines return the name of tty.  tty_name() should NOT
+ * be used in interrupt drivers, since it's not re-entrant.  Use
+ * _tty_name() instead.
+ */
+char *_tty_name(struct tty_struct *tty, char *buf)
+{
+	if (tty)
+		sprintf(buf, "%s%d", tty->driver.name,
+			MINOR(tty->device) - tty->driver.minor_start +
+			tty->driver.name_base);
+	else
+		strcpy(buf, "NULL tty");
+	return buf;
+}
+
+char *tty_name(struct tty_struct *tty)
+{
+	static char buf[64];
+
+	return(_tty_name(tty, buf));
+}
+
+inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
+			      const char *routine)
+{
+#ifdef TTY_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for tty struct (%s) in %s\n";
+	static const char *badtty =
+		"Warning: null TTY for (%s) in %s\n";
+
+	if (!tty) {
+		printk(badtty, kdevname(device), routine);
+		return 1;
+	}
+	if (tty->magic != TTY_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+static int check_tty_count(struct tty_struct *tty, const char *routine)
+{
+#ifdef CHECK_TTY_COUNT
+	struct file *f;
+	int i, count = 0;
+	
+	for (f = first_file, i=0; i<nr_files; i++, f = f->f_next) {
+		if (!f->f_count)
+			continue;
+		if (f->private_data == tty) {
+			count++;
+		}
+	}
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_SLAVE &&
+	    tty->link && tty->link->count)
+		count++;
+	if (tty->count != count) {
+		printk("Warning: dev (%s) tty->count(%d) != #fd's(%d) in %s\n",
+		       kdevname(tty->device), tty->count, count, routine);
+		return count;
+       }	
+#endif
+	return 0;
+}
+
+int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
+{
+	if (disc < N_TTY || disc >= NR_LDISCS)
+		return -EINVAL;
+	
+	if (new_ldisc) {
+		ldiscs[disc] = *new_ldisc;
+		ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
+		ldiscs[disc].num = disc;
+	} else
+		memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
+	
+	return 0;
+}
+
+/* Set the discipline of a tty line. */
+static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
+{
+	int	retval = 0;
+	struct	tty_ldisc o_ldisc;
+
+	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
+	    !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
+		return -EINVAL;
+
+	if (tty->ldisc.num == ldisc)
+		return 0;	/* We are already in the desired discipline */
+	o_ldisc = tty->ldisc;
+
+	tty_wait_until_sent(tty, 0);
+	
+	/* Shutdown the current discipline. */
+	if (tty->ldisc.close)
+		(tty->ldisc.close)(tty);
+
+	/* Now set up the new line discipline. */
+	tty->ldisc = ldiscs[ldisc];
+	tty->termios->c_line = ldisc;
+	if (tty->ldisc.open)
+		retval = (tty->ldisc.open)(tty);
+	if (retval < 0) {
+		tty->ldisc = o_ldisc;
+		tty->termios->c_line = tty->ldisc.num;
+		if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
+			tty->ldisc = ldiscs[N_TTY];
+			tty->termios->c_line = N_TTY;
+			if (tty->ldisc.open) {
+				int r = tty->ldisc.open(tty);
+
+				if (r < 0)
+					panic("Couldn't open N_TTY ldisc for "
+					      "%s --- error %d.",
+					      tty_name(tty), r);
+			}
+		}
+	}
+	if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc)
+		tty->driver.set_ldisc(tty);
+	return retval;
+}
+
+/*
+ * This routine returns a tty driver structure, given a device number
+ */
+struct tty_driver *get_tty_driver(kdev_t device)
+{
+	int	major, minor;
+	struct tty_driver *p;
+	
+	minor = MINOR(device);
+	major = MAJOR(device);
+
+	for (p = tty_drivers; p; p = p->next) {
+		if (p->major != major)
+			continue;
+		if (minor < p->minor_start)
+			continue;
+		if (minor >= p->minor_start + p->num)
+			continue;
+		return p;
+	}
+	return NULL;
+}
+
+/*
+ * If we try to write to, or set the state of, a terminal and we're
+ * not in the foreground, send a SIGTTOU.  If the signal is blocked or
+ * ignored, go ahead and perform the operation.  (POSIX 7.2)
+ */
+int tty_check_change(struct tty_struct * tty)
+{
+	if (current->tty != tty)
+		return 0;
+	if (tty->pgrp <= 0) {
+		printk("tty_check_change: tty->pgrp <= 0!\n");
+		return 0;
+	}
+	if (current->pgrp == tty->pgrp)
+		return 0;
+	if (is_ignored(SIGTTOU))
+		return 0;
+	if (is_orphaned_pgrp(current->pgrp))
+		return -EIO;
+	(void) kill_pg(current->pgrp,SIGTTOU,1);
+	return -ERESTARTSYS;
+}
+
+static int hung_up_tty_read(struct inode * inode, struct file * file, char * buf, int count)
+{
+	return 0;
+}
+
+static int hung_up_tty_write(struct inode * inode, struct file * file, const char * buf, int count)
+{
+	return -EIO;
+}
+
+static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
+{
+	return 1;
+}
+
+static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
+			     unsigned int cmd, unsigned long arg)
+{
+	return -EIO;
+}
+
+static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
+{
+	return -ESPIPE;
+}
+
+static struct file_operations tty_fops = {
+	tty_lseek,
+	tty_read,
+	tty_write,
+	NULL,		/* tty_readdir */
+	tty_select,
+	tty_ioctl,
+	NULL,		/* tty_mmap */
+	tty_open,
+	tty_release,
+	NULL,		/* tty_fsync */
+	tty_fasync
+};
+
+static struct file_operations hung_up_tty_fops = {
+	tty_lseek,
+	hung_up_tty_read,
+	hung_up_tty_write,
+	NULL,		/* hung_up_tty_readdir */
+	hung_up_tty_select,
+	hung_up_tty_ioctl,
+	NULL,		/* hung_up_tty_mmap */
+	NULL,		/* hung_up_tty_open */
+	tty_release,	/* hung_up_tty_release */
+	NULL,		/* hung_up_tty_fsync  */
+	NULL		/* hung_up_tty_fasync */
+};
+
+void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
+{
+	int i;
+	struct file * filp;
+	struct task_struct *p;
+
+	if (!tty)
+		return;
+	check_tty_count(tty, "do_tty_hangup");
+	for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
+		if (!filp->f_count)
+			continue;
+		if (filp->private_data != tty)
+			continue;
+		if (filp->f_inode
+		    && filp->f_inode->i_rdev == CONSOLE_DEV)
+			continue;
+		if (filp->f_op != &tty_fops)
+			continue;
+		tty_fasync(filp->f_inode, filp, 0);
+		filp->f_op = fops;
+	}
+	
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	wake_up_interruptible(&tty->write_wait);
+	wake_up_interruptible(&tty->read_wait);
+
+	/*
+	 * Shutdown the current line discipline, and reset it to
+	 * N_TTY.
+	 */
+	if (tty->ldisc.num != ldiscs[N_TTY].num) {
+		if (tty->ldisc.close)
+			(tty->ldisc.close)(tty);
+		tty->ldisc = ldiscs[N_TTY];
+		tty->termios->c_line = N_TTY;
+		if (tty->ldisc.open) {
+			i = (tty->ldisc.open)(tty);
+			if (i < 0)
+				printk("do_tty_hangup: N_TTY open: error %d\n",
+				       -i);
+		}
+	}
+	
+ 	for_each_task(p) {
+		if ((tty->session > 0) && (p->session == tty->session) &&
+		    p->leader) {
+			send_sig(SIGHUP,p,1);
+			send_sig(SIGCONT,p,1);
+			if (tty->pgrp > 0)
+				p->tty_old_pgrp = tty->pgrp;
+		}
+		if (p->tty == tty)
+			p->tty = NULL;
+	}
+	tty->flags = 0;
+	tty->session = 0;
+	tty->pgrp = -1;
+	tty->ctrl_status = 0;
+	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
+		*tty->termios = tty->driver.init_termios;
+	if (tty->driver.hangup)
+		(tty->driver.hangup)(tty);
+}
+
+void tty_hangup(struct tty_struct * tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+	printk("%s hangup...\n", tty_name(tty));
+#endif
+	do_tty_hangup(tty, &hung_up_tty_fops);
+}
+
+void tty_vhangup(struct tty_struct * tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+	printk("%s vhangup...\n", tty_name(tty));
+#endif
+	do_tty_hangup(tty, &hung_up_tty_fops);
+}
+
+int tty_hung_up_p(struct file * filp)
+{
+	return (filp->f_op == &hung_up_tty_fops);
+}
+
+/*
+ * This function is typically called only by the session leader, when
+ * it wants to disassociate itself from its controlling tty.
+ *
+ * It performs the following functions:
+ * 	(1)  Sends a SIGHUP and SIGCONT to the foreground process group
+ * 	(2)  Clears the tty from being controlling the session
+ * 	(3)  Clears the controlling tty for all processes in the
+ * 		session group.
+ */
+void disassociate_ctty(int priv)
+{
+	struct tty_struct *tty = current->tty;
+	struct task_struct *p;
+
+	if (!tty) {
+		if (current->tty_old_pgrp) {
+			kill_pg(current->tty_old_pgrp, SIGHUP, priv);
+			kill_pg(current->tty_old_pgrp, SIGCONT, priv);
+		}
+		return;
+	}
+	if (tty->pgrp > 0) {
+		kill_pg(tty->pgrp, SIGHUP, priv);
+		kill_pg(tty->pgrp, SIGCONT, priv);
+	}
+
+	current->tty_old_pgrp = 0;
+	tty->session = 0;
+	tty->pgrp = -1;
+
+	for_each_task(p)
+	  	if (p->session == current->session)
+			p->tty = NULL;
+}
+
+/*
+ * Sometimes we want to wait until a particular VT has been activated. We
+ * do it in a very simple manner. Everybody waits on a single queue and
+ * get woken up at once. Those that are satisfied go on with their business,
+ * while those not ready go back to sleep. Seems overkill to add a wait
+ * to each vt just for this - usually this does nothing!
+ */
+static struct wait_queue *vt_activate_queue = NULL;
+
+/*
+ * Sleeps until a vt is activated, or the task is interrupted. Returns
+ * 0 if activation, -1 if interrupted.
+ */
+int vt_waitactive(void)
+{
+	interruptible_sleep_on(&vt_activate_queue);
+	return (current->signal & ~current->blocked) ? -1 : 0;
+}
+
+#define vt_wake_waitactive() wake_up(&vt_activate_queue)
+
+void reset_vc(unsigned int new_console)
+{
+	vt_cons[new_console]->vc_mode = KD_TEXT;
+	kbd_table[new_console].kbdmode = VC_XLATE;
+	vt_cons[new_console]->vt_mode.mode = VT_AUTO;
+	vt_cons[new_console]->vt_mode.waitv = 0;
+	vt_cons[new_console]->vt_mode.relsig = 0;
+	vt_cons[new_console]->vt_mode.acqsig = 0;
+	vt_cons[new_console]->vt_mode.frsig = 0;
+	vt_cons[new_console]->vt_pid = -1;
+	vt_cons[new_console]->vt_newvt = -1;
+	reset_palette (new_console) ;
+}
+
+/*
+ * Performs the back end of a vt switch
+ */
+void complete_change_console(unsigned int new_console)
+{
+	unsigned char old_vc_mode;
+
+        if (new_console == fg_console)
+                return;
+        if (!vc_cons_allocated(new_console))
+                return;
+	last_console = fg_console;
+
+	/*
+	 * If we're switching, we could be going from KD_GRAPHICS to
+	 * KD_TEXT mode or vice versa, which means we need to blank or
+	 * unblank the screen later.
+	 */
+	old_vc_mode = vt_cons[fg_console]->vc_mode;
+	update_screen(new_console);
+
+	/*
+	 * If this new console is under process control, send it a signal
+	 * telling it that it has acquired. Also check if it has died and
+	 * clean up (similar to logic employed in change_console())
+	 */
+	if (vt_cons[new_console]->vt_mode.mode == VT_PROCESS)
+	{
+		/*
+		 * Send the signal as privileged - kill_proc() will
+		 * tell us if the process has gone or something else
+		 * is awry
+		 */
+		if (kill_proc(vt_cons[new_console]->vt_pid,
+			      vt_cons[new_console]->vt_mode.acqsig,
+			      1) != 0)
+		{
+		/*
+		 * The controlling process has died, so we revert back to
+		 * normal operation. In this case, we'll also change back
+		 * to KD_TEXT mode. I'm not sure if this is strictly correct
+		 * but it saves the agony when the X server dies and the screen
+		 * remains blanked due to KD_GRAPHICS! It would be nice to do
+		 * this outside of VT_PROCESS but there is no single process
+		 * to account for and tracking tty count may be undesirable.
+		 */
+		        reset_vc(new_console);
+		}
+	}
+
+	/*
+	 * We do this here because the controlling process above may have
+	 * gone, and so there is now a new vc_mode
+	 */
+	if (old_vc_mode != vt_cons[new_console]->vc_mode)
+	{
+		if (vt_cons[new_console]->vc_mode == KD_TEXT)
+			do_unblank_screen();
+		else
+			do_blank_screen(1);
+	}
+
+	/* Set the colour palette for this VT */
+	if (vt_cons[new_console]->vc_mode == KD_TEXT)
+		set_palette() ;
+	
+	/*
+	 * Wake anyone waiting for their VT to activate
+	 */
+	vt_wake_waitactive();
+	return;
+}
+
+/*
+ * Performs the front-end of a vt switch
+ */
+void change_console(unsigned int new_console)
+{
+        if (new_console == fg_console)
+                return;
+        if (!vc_cons_allocated(new_console))
+		return;
+
+	/*
+	 * If this vt is in process mode, then we need to handshake with
+	 * that process before switching. Essentially, we store where that
+	 * vt wants to switch to and wait for it to tell us when it's done
+	 * (via VT_RELDISP ioctl).
+	 *
+	 * We also check to see if the controlling process still exists.
+	 * If it doesn't, we reset this vt to auto mode and continue.
+	 * This is a cheap way to track process control. The worst thing
+	 * that can happen is: we send a signal to a process, it dies, and
+	 * the switch gets "lost" waiting for a response; hopefully, the
+	 * user will try again, we'll detect the process is gone (unless
+	 * the user waits just the right amount of time :-) and revert the
+	 * vt to auto control.
+	 */
+	if (vt_cons[fg_console]->vt_mode.mode == VT_PROCESS)
+	{
+		/*
+		 * Send the signal as privileged - kill_proc() will
+		 * tell us if the process has gone or something else
+		 * is awry
+		 */
+		if (kill_proc(vt_cons[fg_console]->vt_pid,
+			      vt_cons[fg_console]->vt_mode.relsig,
+			      1) == 0)
+		{
+			/*
+			 * It worked. Mark the vt to switch to and
+			 * return. The process needs to send us a
+			 * VT_RELDISP ioctl to complete the switch.
+			 */
+			vt_cons[fg_console]->vt_newvt = new_console;
+			return;
+		}
+
+		/*
+		 * The controlling process has died, so we revert back to
+		 * normal operation. In this case, we'll also change back
+		 * to KD_TEXT mode. I'm not sure if this is strictly correct
+		 * but it saves the agony when the X server dies and the screen
+		 * remains blanked due to KD_GRAPHICS! It would be nice to do
+		 * this outside of VT_PROCESS but there is no single process
+		 * to account for and tracking tty count may be undesirable.
+		 */
+		reset_vc(fg_console);
+
+		/*
+		 * Fall through to normal (VT_AUTO) handling of the switch...
+		 */
+	}
+
+	/*
+	 * Ignore all switches in KD_GRAPHICS+VT_AUTO mode
+	 */
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+		return;
+
+	complete_change_console(new_console);
+}
+
+void wait_for_keypress(void)
+{
+	sleep_on(&keypress_wait);
+}
+
+void stop_tty(struct tty_struct *tty)
+{
+	if (tty->stopped)
+		return;
+	tty->stopped = 1;
+	if (tty->link && tty->link->packet) {
+		tty->ctrl_status &= ~TIOCPKT_START;
+		tty->ctrl_status |= TIOCPKT_STOP;
+		wake_up_interruptible(&tty->link->read_wait);
+	}
+	if (tty->driver.stop)
+		(tty->driver.stop)(tty);
+}
+
+void start_tty(struct tty_struct *tty)
+{
+	if (!tty->stopped)
+		return;
+	tty->stopped = 0;
+	if (tty->link && tty->link->packet) {
+		tty->ctrl_status &= ~TIOCPKT_STOP;
+		tty->ctrl_status |= TIOCPKT_START;
+		wake_up_interruptible(&tty->link->read_wait);
+	}
+	if (tty->driver.start)
+		(tty->driver.start)(tty);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	wake_up_interruptible(&tty->write_wait);
+}
+
+static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
+{
+	int i;
+	struct tty_struct * tty;
+
+	tty = (struct tty_struct *)file->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
+		return -EIO;
+	if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))
+		return -EIO;
+
+	/* This check not only needs to be done before reading, but also
+	   whenever read_chan() gets woken up after sleeping, so I've
+	   moved it to there.  This should only be done for the N_TTY
+	   line discipline, anyway.  Same goes for write_chan(). -- jlc. */
+#if 0
+	if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
+	    (tty->pgrp > 0) &&
+	    (current->tty == tty) &&
+	    (tty->pgrp != current->pgrp))
+		if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
+			return -EIO;
+		else {
+			(void) kill_pg(current->pgrp, SIGTTIN, 1);
+			return -ERESTARTSYS;
+		}
+#endif
+	if (tty->ldisc.read)
+		/* XXX casts are for what kernel-wide prototypes should be. */
+		i = (tty->ldisc.read)(tty,file,(unsigned char *)buf,(unsigned int)count);
+	else
+		i = -EIO;
+	if (i > 0)
+		inode->i_atime = CURRENT_TIME;
+	return i;
+}
+
+static int tty_write(struct inode * inode, struct file * file, const char * buf, int count)
+{
+	int i, is_console;
+	struct tty_struct * tty;
+
+	is_console = (inode->i_rdev == CONSOLE_DEV);
+
+	if (is_console && redirect)
+		tty = redirect;
+	else
+		tty = (struct tty_struct *)file->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
+		return -EIO;
+	if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))
+		return -EIO;
+#if 0
+	if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
+	    (current->tty == tty) && (tty->pgrp != current->pgrp)) {
+		if (is_orphaned_pgrp(current->pgrp))
+			return -EIO;
+		if (!is_ignored(SIGTTOU)) {
+			(void) kill_pg(current->pgrp, SIGTTOU, 1);
+			return -ERESTARTSYS;
+		}
+	}
+#endif
+	if (tty->ldisc.write)
+		/* XXX casts are for what kernel-wide prototypes should be. */
+		i = (tty->ldisc.write)(tty,file,(const unsigned char *)buf,(unsigned int)count);
+	else
+		i = -EIO;
+	if (i > 0)
+		inode->i_mtime = CURRENT_TIME;
+	return i;
+}
+
+/*
+ * This is so ripe with races that you should *really* not touch this
+ * unless you know exactly what you are doing. All the changes have to be
+ * made atomically, or there may be incorrect pointers all over the place.
+ */
+static int init_dev(kdev_t device, struct tty_struct **ret_tty)
+{
+	struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
+	struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
+	struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
+	struct tty_driver *driver;	
+	int retval;
+	int idx;
+
+	driver = get_tty_driver(device);
+	if (!driver)
+		return -ENODEV;
+
+	idx = MINOR(device) - driver->minor_start;
+	tty = o_tty = NULL;
+	tp = o_tp = NULL;
+	ltp = o_ltp = NULL;
+	o_tty_loc = NULL;
+	o_tp_loc = o_ltp_loc = NULL;
+
+	tty_loc = &driver->table[idx];
+	tp_loc = &driver->termios[idx];
+	ltp_loc = &driver->termios_locked[idx];
+
+repeat:
+	retval = -EAGAIN;
+	if (driver->type == TTY_DRIVER_TYPE_PTY &&
+	    driver->subtype == PTY_TYPE_MASTER &&
+	    *tty_loc && (*tty_loc)->count)
+		goto end_init;
+	retval = -ENOMEM;
+	if (!*tty_loc && !tty) {
+		if (!(tty = (struct tty_struct*) kmalloc(sizeof(struct tty_struct),GFP_KERNEL)))
+			goto end_init;
+		initialize_tty_struct(tty);
+		tty->device = device;
+		tty->driver = *driver;
+		goto repeat;
+	}
+	if (!*tp_loc && !tp) {
+		tp = (struct termios *) kmalloc(sizeof(struct termios),
+						GFP_KERNEL);
+		if (!tp)
+			goto end_init;
+		*tp = driver->init_termios;
+		goto repeat;
+	}
+	if (!*ltp_loc && !ltp) {
+		ltp = (struct termios *) kmalloc(sizeof(struct termios),
+						 GFP_KERNEL);
+		if (!ltp)
+			goto end_init;
+		memset(ltp, 0, sizeof(struct termios));
+		goto repeat;
+	}
+	if (driver->type == TTY_DRIVER_TYPE_PTY) {
+		o_tty_loc = &driver->other->table[idx];
+		o_tp_loc = &driver->other->termios[idx];
+		o_ltp_loc = &driver->other->termios_locked[idx];
+
+		if (!*o_tty_loc && !o_tty) {
+			kdev_t 	o_device;
+			
+			o_tty = (struct tty_struct *) kmalloc (sizeof(struct tty_struct),
+				GFP_KERNEL);
+			if (!o_tty)
+				goto end_init;
+			o_device = MKDEV(driver->other->major,
+					 driver->other->minor_start + idx);
+			initialize_tty_struct(o_tty);
+			o_tty->device = o_device;
+			o_tty->driver = *driver->other;
+			goto repeat;
+		}
+		if (!*o_tp_loc && !o_tp) {
+			o_tp = (struct termios *)
+				kmalloc(sizeof(struct termios), GFP_KERNEL);
+			if (!o_tp)
+				goto end_init;
+			*o_tp = driver->other->init_termios;
+			goto repeat;
+		}
+		if (!*o_ltp_loc && !o_ltp) {
+			o_ltp = (struct termios *)
+				kmalloc(sizeof(struct termios), GFP_KERNEL);
+			if (!o_ltp)
+				goto end_init;
+			memset(o_ltp, 0, sizeof(struct termios));
+			goto repeat;
+		}
+		
+	}
+	/* Now we have allocated all the structures: update all the pointers.. */
+	if (!*tp_loc) {
+		*tp_loc = tp;
+		tp = NULL;
+	}
+	if (!*ltp_loc) {
+		*ltp_loc = ltp;
+		ltp = NULL;
+	}
+	if (!*tty_loc) {
+		tty->termios = *tp_loc;
+		tty->termios_locked = *ltp_loc;
+		*tty_loc = tty;
+		(*driver->refcount)++;
+		(*tty_loc)->count++;
+		if (tty->ldisc.open) {
+			retval = (tty->ldisc.open)(tty);
+			if (retval < 0) {
+				(*tty_loc)->count--;
+				tty = NULL;
+				goto end_init;
+			}
+		}
+		tty = NULL;
+	} else {
+		if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
+			printk("Attempt to open closing tty %s.\n",
+			       tty_name(*tty_loc));
+			printk("Ack!!!!  This should never happen!!\n");
+			return -EINVAL;
+		}
+		(*tty_loc)->count++;
+	}
+	if (driver->type == TTY_DRIVER_TYPE_PTY) {
+		if (!*o_tp_loc) {
+			*o_tp_loc = o_tp;
+			o_tp = NULL;
+		}
+		if (!*o_ltp_loc) {
+			*o_ltp_loc = o_ltp;
+			o_ltp = NULL;
+		}
+		if (!*o_tty_loc) {
+			o_tty->termios = *o_tp_loc;
+			o_tty->termios_locked = *o_ltp_loc;
+			*o_tty_loc = o_tty;
+			(*driver->other->refcount)++;
+			if (o_tty->ldisc.open) {
+				retval = (o_tty->ldisc.open)(o_tty);
+				if (retval < 0) {
+					(*tty_loc)->count--;
+					o_tty = NULL;
+					goto end_init;
+				}
+			}
+			o_tty = NULL;
+		}
+		(*tty_loc)->link = *o_tty_loc;
+		(*o_tty_loc)->link = *tty_loc;
+		if (driver->subtype == PTY_TYPE_MASTER)
+			(*o_tty_loc)->count++;
+	}
+	(*tty_loc)->driver = *driver;
+	*ret_tty = *tty_loc;
+	retval = 0;
+end_init:
+	if (tty)
+		kfree_s(tty, sizeof(struct tty_struct));
+	if (o_tty)
+		kfree_s(o_tty, sizeof(struct tty_struct));
+	if (tp)
+		kfree_s(tp, sizeof(struct termios));
+	if (o_tp)
+		kfree_s(o_tp, sizeof(struct termios));
+	if (ltp)
+		kfree_s(ltp, sizeof(struct termios));
+	if (o_ltp)
+		kfree_s(o_ltp, sizeof(struct termios));
+	return retval;
+}
+
+/*
+ * Even releasing the tty structures is a tricky business.. We have
+ * to be very careful that the structures are all released at the
+ * same time, as interrupts might otherwise get the wrong pointers.
+ */
+static void release_dev(struct file * filp)
+{
+	struct tty_struct *tty, *o_tty;
+	struct termios *tp, *o_tp, *ltp, *o_ltp;
+	struct task_struct **p;
+	int	idx;
+	
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
+		return;
+
+	check_tty_count(tty, "release_dev");
+
+	tty_fasync(filp->f_inode, filp, 0);
+
+	tp = tty->termios;
+	ltp = tty->termios_locked;
+
+	idx = MINOR(tty->device) - tty->driver.minor_start;
+#ifdef TTY_PARANOIA_CHECK
+	if (idx < 0 || idx >= tty->driver.num) {
+		printk("release_dev: bad idx when trying to free (%s)\n",
+		       kdevname(tty->device));
+		return;
+	}
+	if (tty != tty->driver.table[idx]) {
+		printk("release_dev: driver.table[%d] not tty for (%s)\n",
+		       idx, kdevname(tty->device));
+		return;
+	}
+	if (tp != tty->driver.termios[idx]) {
+		printk("release_dev: driver.termios[%d] not termios for (%s)\n",
+		       idx, kdevname(tty->device));
+		return;
+	}
+	if (ltp != tty->driver.termios_locked[idx]) {
+		printk("release_dev: driver.termios_locked[%d] not termios_locked for (%s)\n",
+		       idx, kdevname(tty->device));
+		return;
+	}
+#endif
+
+#ifdef TTY_DEBUG_HANGUP
+	printk("release_dev of %s (tty count=%d)...", tty_name(tty),
+	       tty->count);
+#endif
+
+	o_tty = tty->link;
+	o_tp = (o_tty) ? o_tty->termios : NULL;
+	o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
+
+#ifdef TTY_PARANOIA_CHECK
+	if (tty->driver.other) {
+		if (o_tty != tty->driver.other->table[idx]) {
+			printk("release_dev: other->table[%d] not o_tty for ("
+			       "%s)\n",
+			       idx, kdevname(tty->device));
+			return;
+		}
+		if (o_tp != tty->driver.other->termios[idx]) {
+			printk("release_dev: other->termios[%d] not o_termios for ("
+			       "%s)\n",
+			       idx, kdevname(tty->device));
+			return;
+		}
+		if (o_ltp != tty->driver.other->termios_locked[idx]) {
+			printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
+			       "%s)\n",
+			       idx, kdevname(tty->device));
+			return;
+		}
+
+		if (o_tty->link != tty) {
+			printk("release_dev: bad pty pointers\n");
+			return;
+		}
+	}
+#endif
+	
+	if (tty->driver.close)
+		tty->driver.close(tty, filp);
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER) {
+		if (--tty->link->count < 0) {
+			printk("release_dev: bad pty slave count (%d) for %s\n",
+			       tty->count, tty_name(tty));
+			tty->link->count = 0;
+		}
+	}
+	if (--tty->count < 0) {
+		printk("release_dev: bad tty->count (%d) for %s\n",
+		       tty->count, tty_name(tty));
+		tty->count = 0;
+	}
+	if (tty->count)
+		return;
+
+	/*
+	 * We're committed; at this point, we must not block!
+	 */
+	if (o_tty) {
+		if (o_tty->count)
+			return;
+		tty->driver.other->table[idx] = NULL;
+		tty->driver.other->termios[idx] = NULL;
+		kfree_s(o_tp, sizeof(struct termios));
+	}
+	
+#ifdef TTY_DEBUG_HANGUP
+	printk("freeing tty structure...");
+#endif
+	tty->flags |= (1 << TTY_CLOSING);
+
+	/*
+	 * Make sure there aren't any processes that still think this
+	 * tty is their controlling tty.
+	 */
+	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+		if (*p == 0)
+			continue;
+		if ((*p)->tty == tty)
+			(*p)->tty = NULL;
+		if (o_tty && (*p)->tty == o_tty)
+			(*p)->tty = NULL;
+	}
+
+	/*
+	 * Shutdown the current line discipline, and reset it to
+	 * N_TTY.
+	 */
+	if (tty->ldisc.close)
+		(tty->ldisc.close)(tty);
+	tty->ldisc = ldiscs[N_TTY];
+	tty->termios->c_line = N_TTY;
+	if (o_tty) {
+		if (o_tty->ldisc.close)
+			(o_tty->ldisc.close)(o_tty);
+		o_tty->ldisc = ldiscs[N_TTY];
+	}
+	
+	tty->driver.table[idx] = NULL;
+	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+		tty->driver.termios[idx] = NULL;
+		kfree_s(tp, sizeof(struct termios));
+	}
+	if (tty == redirect || o_tty == redirect)
+		redirect = NULL;
+	/*
+	 * Make sure that the tty's task queue isn't activated.  If it
+	 * is, take it out of the linked list.
+	 */
+	cli();
+	if (tty->flip.tqueue.sync) {
+		struct tq_struct *tq, *prev;
+
+		for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+			if (tq == &tty->flip.tqueue) {
+				if (prev)
+					prev->next = tq->next;
+				else
+					tq_timer = tq->next;
+				break;
+			}
+		}
+	}
+	sti();
+	tty->magic = 0;
+	(*tty->driver.refcount)--;
+	kfree_s(tty, sizeof(struct tty_struct));
+	filp->private_data = 0;
+	if (o_tty) {
+		o_tty->magic = 0;
+		(*o_tty->driver.refcount)--;
+		kfree_s(o_tty, sizeof(struct tty_struct));
+	}
+}
+
+/*
+ * tty_open and tty_release keep up the tty count that contains the
+ * number of opens done on a tty. We cannot use the inode-count, as
+ * different inodes might point to the same tty.
+ *
+ * Open-counting is needed for pty masters, as well as for keeping
+ * track of serial lines: DTR is dropped when the last close happens.
+ * (This is not done solely through tty->count, now.  - Ted 1/27/92)
+ *
+ * The termios state of a pty is reset on first open so that
+ * settings don't persist across reuse.
+ */
+static int tty_open(struct inode * inode, struct file * filp)
+{
+	struct tty_struct *tty;
+	int minor;
+	int noctty, retval;
+	kdev_t device;
+
+retry_open:
+	noctty = filp->f_flags & O_NOCTTY;
+	device = inode->i_rdev;
+	if (device == TTY_DEV) {
+		if (!current->tty)
+			return -ENXIO;
+		device = current->tty->device;
+		/* noctty = 1; */
+	}
+	if (device == CONSOLE_DEV) {
+		device = MKDEV(TTY_MAJOR, fg_console+1);
+		noctty = 1;
+	}
+	minor = MINOR(device);
+	
+	retval = init_dev(device, &tty);
+	if (retval)
+		return retval;
+	filp->private_data = tty;
+	check_tty_count(tty, "tty_open");
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER)
+		noctty = 1;
+#ifdef TTY_DEBUG_HANGUP
+	printk("opening %s...", tty_name(tty));
+#endif
+	if (tty->driver.open)
+		retval = tty->driver.open(tty, filp);
+	else
+		retval = -ENODEV;
+
+	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
+		retval = -EBUSY;
+
+	if (retval) {
+#ifdef TTY_DEBUG_HANGUP
+		printk("error %d in opening %s...", retval, tty_name(tty));
+#endif
+
+		release_dev(filp);
+		if (retval != -ERESTARTSYS)
+			return retval;
+		if (current->signal & ~current->blocked)
+			return retval;
+		schedule();
+		/*
+		 * Need to reset f_op in case a hangup happened.
+		 */
+		filp->f_op = &tty_fops;
+		goto retry_open;
+	}
+	if (!noctty &&
+	    current->leader &&
+	    !current->tty &&
+	    tty->session == 0) {
+		current->tty = tty;
+		current->tty_old_pgrp = 0;
+		tty->session = current->session;
+		tty->pgrp = current->pgrp;
+	}
+	return 0;
+}
+
+/*
+ * Note that releasing a pty master also releases the child, so
+ * we have to make the redirection checks after that and on both
+ * sides of a pty.
+ */
+static void tty_release(struct inode * inode, struct file * filp)
+{
+	release_dev(filp);
+}
+
+static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
+{
+	struct tty_struct * tty;
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_select"))
+		return 0;
+
+	if (tty->ldisc.select)
+		return (tty->ldisc.select)(tty, inode, filp, sel_type, wait);
+	return 0;
+}
+
+/*
+ * fasync_helper() is used by some character device drivers (mainly mice)
+ * to set up the fasync queue. It returns negative on error, 0 if it did
+ * no changes, and positive if it added/deleted the entry.
+ */
+int fasync_helper(struct inode * inode, struct file * filp, int on, struct fasync_struct **fapp)
+{
+	struct fasync_struct *fa, **fp;
+	unsigned long flags;
+
+	for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
+		if (fa->fa_file == filp)
+			break;
+	}
+
+	if (on) {
+		if (fa)
+			return 0;
+		fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
+		if (!fa)
+			return -ENOMEM;
+		fa->magic = FASYNC_MAGIC;
+		fa->fa_file = filp;
+		save_flags(flags);
+		cli();
+		fa->fa_next = *fapp;
+		*fapp = fa;
+		restore_flags(flags);
+		return 1;
+	}
+	if (!fa)
+		return 0;
+	save_flags(flags);
+	cli();
+	*fp = fa->fa_next;
+	restore_flags(flags);
+	kfree(fa);
+	return 1;
+}
+
+static int tty_fasync(struct inode * inode, struct file * filp, int on)
+{
+	struct tty_struct * tty;
+	int retval;
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
+		return 0;
+
+	retval = fasync_helper(inode, filp, on, &tty->fasync);
+	if (retval <= 0)
+		return retval;
+
+	if (on) {
+		if (!tty->read_wait)
+			tty->minimum_to_wake = 1;
+		if (filp->f_owner == 0) {
+			if (tty->pgrp)
+				filp->f_owner = -tty->pgrp;
+			else
+				filp->f_owner = current->pid;
+		}
+	} else {
+		if (!tty->fasync && !tty->read_wait)
+			tty->minimum_to_wake = N_TTY_BUF_SIZE;
+	}
+	return 0;	
+}
+
+#if 0
+/*
+ * XXX does anyone use this anymore?!?
+ */
+static int do_get_ps_info(unsigned long arg)
+{
+	struct tstruct {
+		int flag;
+		int present[NR_TASKS];
+		struct task_struct tasks[NR_TASKS];
+	};
+	struct tstruct *ts = (struct tstruct *)arg;
+	struct task_struct **p;
+	char *c, *d;
+	int i, n = 0;
+	
+	i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
+	if (i)
+		return i;
+	for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
+		if (*p)
+		{
+			c = (char *)(*p);
+			d = (char *)(ts->tasks+n);
+			for (i=0 ; i<sizeof(struct task_struct) ; i++)
+				put_user(*c++, d++);
+			put_user(1, ts->present+n);
+		}
+		else	
+			put_user(0, ts->present+n);
+	return(0);			
+}
+#endif
+
+static int tty_ioctl(struct inode * inode, struct file * file,
+		     unsigned int cmd, unsigned long arg)
+{
+	int	retval;
+	struct tty_struct * tty;
+	struct tty_struct * real_tty;
+	struct winsize tmp_ws;
+	pid_t pgrp;
+	unsigned char	ch;
+	char	mbz = 0;
+	
+	tty = (struct tty_struct *)file->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
+		return -EINVAL;
+
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER)
+		real_tty = tty->link;
+	else
+		real_tty = tty;
+
+	switch (cmd) {
+		case TIOCSTI:
+			if ((current->tty != tty) && !suser())
+				return -EPERM;
+			retval = verify_area(VERIFY_READ, (void *) arg, 1);
+			if (retval)
+				return retval;
+			ch = get_user((char *) arg);
+			tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
+			return 0;
+		case TIOCGWINSZ:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (struct winsize));
+			if (retval)
+				return retval;
+			memcpy_tofs((struct winsize *) arg, &tty->winsize,
+				    sizeof (struct winsize));
+			return 0;
+		case TIOCSWINSZ:
+			retval = verify_area(VERIFY_READ, (void *) arg,
+					     sizeof (struct winsize));
+			if (retval)
+				return retval;			
+			memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
+				      sizeof (struct winsize));
+			if (memcmp(&tmp_ws, &tty->winsize,
+				   sizeof(struct winsize))) {
+				if (tty->pgrp > 0)
+					kill_pg(tty->pgrp, SIGWINCH, 1);
+				if ((real_tty->pgrp != tty->pgrp) &&
+				    (real_tty->pgrp > 0))
+					kill_pg(real_tty->pgrp, SIGWINCH, 1);
+			}
+			tty->winsize = tmp_ws;
+			real_tty->winsize = tmp_ws;
+			return 0;
+		case TIOCCONS:
+			if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) {
+				if (!suser())
+					return -EPERM;
+				redirect = NULL;
+				return 0;
+			}
+			if (redirect)
+				return -EBUSY;
+			redirect = real_tty;
+			return 0;
+		case FIONBIO:
+			retval = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+			if (retval)
+				return retval;
+			arg = get_user((unsigned int *) arg);
+			if (arg)
+				file->f_flags |= O_NONBLOCK;
+			else
+				file->f_flags &= ~O_NONBLOCK;
+			return 0;
+		case TIOCEXCL:
+			set_bit(TTY_EXCLUSIVE, &tty->flags);
+			return 0;
+		case TIOCNXCL:
+			clear_bit(TTY_EXCLUSIVE, &tty->flags);
+			return 0;
+		case TIOCNOTTY:
+			if (current->tty != tty)
+				return -ENOTTY;
+			if (current->leader)
+				disassociate_ctty(0);
+			current->tty = NULL;
+			return 0;
+		case TIOCSCTTY:
+			if (current->leader &&
+			    (current->session == tty->session))
+				return 0;
+			/*
+			 * The process must be a session leader and
+			 * not have a controlling tty already.
+			 */
+			if (!current->leader || current->tty)
+				return -EPERM;
+			if (tty->session > 0) {
+				/*
+				 * This tty is already the controlling
+				 * tty for another session group!
+				 */
+				if ((arg == 1) && suser()) {
+					/*
+					 * Steal it away
+					 */
+					struct task_struct *p;
+
+					for_each_task(p)
+						if (p->tty == tty)
+							p->tty = NULL;
+				} else
+					return -EPERM;
+			}
+			current->tty = tty;
+			current->tty_old_pgrp = 0;
+			tty->session = current->session;
+			tty->pgrp = current->pgrp;
+			return 0;
+		case TIOCGPGRP:
+			/*
+			 * (tty == real_tty) is a cheap way of
+			 * testing if the tty is NOT a master pty.
+			 */
+			if (tty == real_tty && current->tty != real_tty)
+				return -ENOTTY;
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (pid_t));
+			if (retval)
+				return retval;
+			put_user(real_tty->pgrp, (pid_t *) arg);
+			return 0;
+		case TIOCSPGRP:
+			retval = tty_check_change(real_tty);
+			if (retval)
+				return retval;
+			if (!current->tty ||
+			    (current->tty != real_tty) ||
+			    (real_tty->session != current->session))
+				return -ENOTTY;
+			pgrp = get_user((pid_t *) arg);
+			if (pgrp < 0)
+				return -EINVAL;
+			if (session_of_pgrp(pgrp) != current->session)
+				return -EPERM;
+			real_tty->pgrp = pgrp;
+			return 0;
+		case TIOCGETD:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (int));
+			if (retval)
+				return retval;
+			put_user(tty->ldisc.num, (int *) arg);
+			return 0;
+		case TIOCSETD:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			arg = get_user((int *) arg);
+			return tty_set_ldisc(tty, arg);
+		case TIOCLINUX:
+			if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
+				return -EINVAL;
+			if (current->tty != tty && !suser())
+				return -EPERM;
+			retval = verify_area(VERIFY_READ, (void *) arg, 1);
+			if (retval)
+				return retval;
+			switch (retval = get_user((char *)arg))
+			{
+				case 0:
+				case 8:
+				case 9:
+					printk("TIOCLINUX (0/8/9) ioctl is gone - use /dev/vcs\n");
+					return -EINVAL;
+#if 0
+				case 1:
+					printk("Deprecated TIOCLINUX (1) ioctl\n");
+					return do_get_ps_info(arg);
+#endif
+				case 2:
+					return set_selection(arg, tty);
+				case 3:
+					return paste_selection(tty);
+				case 4:
+					do_unblank_screen();
+					return 0;
+				case 5:
+					return sel_loadlut(arg);
+				case 6:
+			/*
+			 * Make it possible to react to Shift+Mousebutton.
+			 * Note that 'shift_state' is an undocumented
+			 * kernel-internal variable; programs not closely
+			 * related to the kernel should not use this.
+			 */
+					put_user(shift_state,(char *) arg);
+					return 0;
+				case 7:
+					put_user(mouse_reporting(),(char *) arg);
+					return 0;
+/*				case 10:
+					set_vesa_blanking(arg);
+					return 0;*/
+				default: 
+					return -EINVAL;
+			}
+
+		case TIOCTTYGSTRUCT:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+						sizeof(struct tty_struct));
+			if (retval)
+				return retval;
+			memcpy_tofs((struct tty_struct *) arg,
+				    tty, sizeof(struct tty_struct));
+			return 0;
+		default:
+			if (tty->driver.ioctl) {
+				retval = (tty->driver.ioctl)(tty, file,
+							     cmd, arg);
+				if (retval != -ENOIOCTLCMD)
+					return retval;
+			}
+			if (tty->ldisc.ioctl) {
+				retval = (tty->ldisc.ioctl)(tty, file,
+							    cmd, arg);
+				if (retval != -ENOIOCTLCMD)
+					return retval;
+			}
+			return -EINVAL;
+		}
+}
+
+
+/*
+ * This implements the "Secure Attention Key" ---  the idea is to
+ * prevent trojan horses by killing all processes associated with this
+ * tty when the user hits the "Secure Attention Key".  Required for
+ * super-paranoid applications --- see the Orange Book for more details.
+ * 
+ * This code could be nicer; ideally it should send a HUP, wait a few
+ * seconds, then send a INT, and then a KILL signal.  But you then
+ * have to coordinate with the init process, since all processes associated
+ * with the current tty must be dead before the new getty is allowed
+ * to spawn.
+ */
+void do_SAK( struct tty_struct *tty)
+{
+#ifdef TTY_SOFT_SAK
+	tty_hangup(tty);
+#else
+	struct task_struct **p;
+	int session;
+	int		i;
+	struct file	*filp;
+	
+	if (!tty)
+		return;
+	session  = tty->session;
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+ 	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+		if (!(*p))
+			continue;
+		if (((*p)->tty == tty) ||
+		    ((session > 0) && ((*p)->session == session)))
+			send_sig(SIGKILL, *p, 1);
+		else {
+			for (i=0; i < NR_OPEN; i++) {
+				filp = (*p)->files->fd[i];
+				if (filp && (filp->f_op == &tty_fops) &&
+				    (filp->private_data == tty)) {
+					send_sig(SIGKILL, *p, 1);
+					break;
+				}
+			}
+		}
+	}
+#endif
+}
+
+/*
+ * This routine is called out of the software interrupt to flush data
+ * from the flip buffer to the line discipline.
+ */
+static void flush_to_ldisc(void *private_)
+{
+	struct tty_struct *tty = (struct tty_struct *) private_;
+	unsigned char	*cp;
+	char		*fp;
+	int		count;
+
+	if (tty->flip.buf_num) {
+		cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
+		fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
+		tty->flip.buf_num = 0;
+
+		cli();
+		tty->flip.char_buf_ptr = tty->flip.char_buf;
+		tty->flip.flag_buf_ptr = tty->flip.flag_buf;
+	} else {
+		cp = tty->flip.char_buf;
+		fp = tty->flip.flag_buf;
+		tty->flip.buf_num = 1;
+
+		cli();
+		tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
+		tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
+	}
+	count = tty->flip.count;
+	tty->flip.count = 0;
+	sti();
+	
+#if 0
+	if (count > tty->max_flip_cnt)
+		tty->max_flip_cnt = count;
+#endif
+	tty->ldisc.receive_buf(tty, cp, fp, count);
+}
+
+/*
+ * This subroutine initializes a tty structure.
+ */
+static void initialize_tty_struct(struct tty_struct *tty)
+{
+	memset(tty, 0, sizeof(struct tty_struct));
+	tty->magic = TTY_MAGIC;
+	tty->ldisc = ldiscs[N_TTY];
+	tty->pgrp = -1;
+	tty->flip.char_buf_ptr = tty->flip.char_buf;
+	tty->flip.flag_buf_ptr = tty->flip.flag_buf;
+	tty->flip.tqueue.routine = flush_to_ldisc;
+	tty->flip.tqueue.data = tty;
+}
+
+/*
+ * The default put_char routine if the driver did not define one.
+ */
+void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	tty->driver.write(tty, 0, &ch, 1);
+}
+
+/*
+ * Called by a tty driver to register itself.
+ */
+int tty_register_driver(struct tty_driver *driver)
+{
+	int error;
+
+	if (driver->flags & TTY_DRIVER_INSTALLED)
+		return 0;
+
+	error = register_chrdev(driver->major, driver->name, &tty_fops);
+	if (error < 0)
+		return error;
+	else if(driver->major == 0)
+		driver->major = error;
+
+	if (!driver->put_char)
+		driver->put_char = tty_default_put_char;
+	
+	driver->prev = 0;
+	driver->next = tty_drivers;
+	if (tty_drivers) tty_drivers->prev = driver;
+	tty_drivers = driver;
+	return error;
+}
+
+/*
+ * Called by a tty driver to unregister itself.
+ */
+int tty_unregister_driver(struct tty_driver *driver)
+{
+	int	retval;
+	struct tty_driver *p;
+	int	found = 0;
+	const char *othername = NULL;
+	
+	if (*driver->refcount)
+		return -EBUSY;
+
+	for (p = tty_drivers; p; p = p->next) {
+		if (p == driver)
+			found++;
+		else if (p->major == driver->major)
+			othername = p->name;
+	}
+
+	if (othername == NULL) {
+		retval = unregister_chrdev(driver->major, driver->name);
+		if (retval)
+			return retval;
+	} else
+		register_chrdev(driver->major, othername, &tty_fops);
+
+	if (driver->prev)
+		driver->prev->next = driver->next;
+	else
+		tty_drivers = driver->next;
+	
+	if (driver->next)
+		driver->next->prev = driver->prev;
+
+	return 0;
+}
+
+
+/*
+ * Initialize the console device. This is called *early*, so
+ * we can't necessarily depend on lots of kernel help here.
+ * Just do some early initializations, and do the complex setup
+ * later.
+ */
+long console_init(long kmem_start, long kmem_end)
+{
+	/* Setup the default TTY line discipline. */
+	memset(ldiscs, 0, sizeof(ldiscs));
+	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
+
+	/*
+	 * Set up the standard termios.  Individual tty drivers may 
+	 * deviate from this; this is used as a template.
+	 */
+	memset(&tty_std_termios, 0, sizeof(struct termios));
+	memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
+	tty_std_termios.c_iflag = ICRNL | IXON;
+	tty_std_termios.c_oflag = OPOST | ONLCR;
+	tty_std_termios.c_cflag = B38400 | CS8 | CREAD;
+	tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
+		ECHOCTL | ECHOKE | IEXTEN;
+
+	/*
+	 * set up the console device so that later boot sequences can 
+	 * inform about problems etc..
+	 */
+	return con_init(kmem_start);
+}
+
+/*
+ * Ok, now we can initialize the rest of the tty devices and can count
+ * on memory allocations, interrupts etc..
+ */
+int tty_init(void)
+{
+	if (sizeof(struct tty_struct) > PAGE_SIZE)
+		panic("size of tty structure > PAGE_SIZE!");
+	if (register_chrdev(TTY_MAJOR,"tty",&tty_fops))
+		panic("unable to get major %d for tty device", TTY_MAJOR);
+	if (register_chrdev(TTYAUX_MAJOR,"cua",&tty_fops))
+		panic("unable to get major %d for tty device", TTYAUX_MAJOR);
+
+	kbd_init();
+	rs_init();
+
+	pty_init();
+	vcs_init();
+	return 0;
+}
diff -urNwbB linux/arch/arm/drivers/char/tty_ioctl.c linux.arm/arch/arm/drivers/char/tty_ioctl.c
--- linux/arch/arm/drivers/char/tty_ioctl.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/tty_ioctl.c	Sun Mar  3 12:49:03 1996
@@ -0,0 +1,376 @@
+/*
+ *  linux/drivers/char/tty_ioctl.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *
+ *  Modifications for ARM processor (C) 1995, 1996 Russell King.
+ *
+ * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
+ * which can be dynamically activated and de-activated by the line
+ * discipline handling modules (like SLIP).
+ */
+
+#include <linux/types.h>
+#include <linux/termios.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/tty.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#undef TTY_DEBUG_WAIT_UNTIL_SENT
+
+#undef	DEBUG
+#ifdef DEBUG
+# define	PRINTK(x)	printk (x)
+#else
+# define	PRINTK(x)	/**/
+#endif
+
+/*
+ * Internal flag options for termios setting behavior
+ */
+#define TERMIOS_FLUSH	1
+#define TERMIOS_WAIT	2
+#define TERMIOS_TERMIO	4
+
+void tty_wait_until_sent(struct tty_struct * tty, int timeout)
+{
+	struct wait_queue wait = { current, NULL };
+
+#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
+	printk("%s wait until sent...\n", tty_name(tty));
+#endif
+	if (!tty->driver.chars_in_buffer ||
+	    !tty->driver.chars_in_buffer(tty))
+		return;
+	add_wait_queue(&tty->write_wait, &wait);
+	current->counter = 0;	/* make us low-priority */
+	if (timeout)
+		current->timeout = timeout + jiffies;
+	else
+		current->timeout = (unsigned) -1;
+	do {
+#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
+		printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty));
+#endif
+		current->state = TASK_INTERRUPTIBLE;
+		if (current->signal & ~current->blocked)
+			break;
+		if (!tty->driver.chars_in_buffer(tty))
+			break;
+		schedule();
+	} while (current->timeout);
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&tty->write_wait, &wait);
+}
+
+static void unset_locked_termios(struct termios *termios,
+				 struct termios *old,
+				 struct termios *locked)
+{
+	int	i;
+	
+#define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z)))
+
+	if (!locked) {
+		printk("Warning?!? termios_locked is NULL.\n");
+		return;
+	}
+
+	NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
+	NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
+	NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
+	NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
+	termios->c_line = locked->c_line ? old->c_line : termios->c_line;
+	for (i=0; i < NCCS; i++)
+		termios->c_cc[i] = locked->c_cc[i] ?
+			old->c_cc[i] : termios->c_cc[i];
+}
+
+static int set_termios(struct tty_struct * tty, unsigned long arg, int opt)
+{
+	struct termio tmp_termio;
+	struct termios tmp_termios;
+	struct termios old_termios = *tty->termios;
+	int retval, canon_change;
+
+	retval = tty_check_change(tty);
+	if (retval)
+		return retval;
+
+	if (opt & TERMIOS_TERMIO) {
+		retval = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termio));
+		if (retval)
+			return retval;
+		tmp_termios = *tty->termios;
+		memcpy_fromfs(&tmp_termio, (struct termio *) arg,
+			      sizeof (struct termio));
+
+#define SET_LOW_BITS(x,y)	((x) = (0xffff0000 & (x)) | (y))
+		SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag);
+		SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag);
+		SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag);
+		SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag);
+		memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC);
+#undef SET_LOW_BITS
+	} else {
+		retval = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios));
+		if (retval)
+			return retval;
+		memcpy_fromfs(&tmp_termios, (struct termios *) arg,
+			      sizeof (struct termios));
+	}
+
+	if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	if (opt & TERMIOS_WAIT)
+		tty_wait_until_sent(tty, 0);
+
+	cli();
+	*tty->termios = tmp_termios;
+	unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
+	canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
+	if (canon_change) {
+		memset(&tty->read_flags, 0, sizeof tty->read_flags);
+		tty->canon_head = tty->read_tail;
+		tty->canon_data = 0;
+		tty->erasing = 0;
+	}
+	sti();
+	if (canon_change && !L_ICANON(tty) && tty->read_cnt)
+		/* Get characters left over from canonical mode. */
+		wake_up_interruptible(&tty->read_wait);
+
+	/* see if packet mode change of state */
+
+	if (tty->link && tty->link->packet) {
+		int old_flow = ((old_termios.c_iflag & IXON) &&
+				(old_termios.c_cc[VSTOP] == '\023') &&
+				(old_termios.c_cc[VSTART] == '\021'));
+		int new_flow = (I_IXON(tty) &&
+				STOP_CHAR(tty) == '\023' &&
+				START_CHAR(tty) == '\021');
+		if (old_flow != new_flow) {
+			tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
+			if (new_flow)
+				tty->ctrl_status |= TIOCPKT_DOSTOP;
+			else
+				tty->ctrl_status |= TIOCPKT_NOSTOP;
+			wake_up_interruptible(&tty->link->read_wait);
+		}
+	}
+
+	if (tty->driver.set_termios)
+		(*tty->driver.set_termios)(tty, &old_termios);
+
+	if (tty->ldisc.set_termios)
+		(*tty->ldisc.set_termios)(tty, &old_termios);
+		
+	return 0;
+}
+
+static int get_termio(struct tty_struct * tty, struct termio * termio)
+{
+	int i;
+	struct termio tmp_termio;
+
+	i = verify_area(VERIFY_WRITE, termio, sizeof (struct termio));
+	if (i)
+		return i;
+	tmp_termio.c_iflag = tty->termios->c_iflag;
+	tmp_termio.c_oflag = tty->termios->c_oflag;
+	tmp_termio.c_cflag = tty->termios->c_cflag;
+	tmp_termio.c_lflag = tty->termios->c_lflag;
+	tmp_termio.c_line = tty->termios->c_line;
+	for(i=0 ; i < NCC ; i++)
+		tmp_termio.c_cc[i] = tty->termios->c_cc[i];
+	memcpy_tofs(termio, &tmp_termio, sizeof (struct termio));
+	return 0;
+}
+
+static unsigned long inq_canon(struct tty_struct * tty)
+{
+	int nr, head, tail;
+
+	if (!tty->canon_data || !tty->read_buf)
+		return 0;
+	head = tty->canon_head;
+	tail = tty->read_tail;
+	nr = (head - tail) & (N_TTY_BUF_SIZE-1);
+	/* Skip EOF-chars.. */
+	while (head != tail) {
+		if (test_bit(tail, &tty->read_flags) &&
+		    tty->read_buf[tail] == __DISABLED_CHAR)
+			nr--;
+		tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+	}
+	return nr;
+}
+
+int n_tty_ioctl(struct tty_struct * tty, struct file * file,
+		       unsigned int cmd, unsigned long arg)
+{
+	struct tty_struct * real_tty;
+	int retval;
+	int opt = 0;
+
+	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+	    tty->driver.subtype == PTY_TYPE_MASTER)
+		real_tty = tty->link;
+	else
+		real_tty = tty;
+
+	switch (cmd) {
+		case TCGETS:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (struct termios));
+			if (retval)
+				return retval;
+			memcpy_tofs((struct termios *) arg,
+				    real_tty->termios,
+				    sizeof (struct termios));
+			return 0;
+		case TCSETSF:
+			opt |= TERMIOS_FLUSH;
+		case TCSETSW:
+			opt |= TERMIOS_WAIT;
+		case TCSETS:
+			return set_termios(real_tty, arg, opt);
+		case TCGETA:
+			return get_termio(real_tty,(struct termio *) arg);
+		case TCSETAF:
+			opt |= TERMIOS_FLUSH;
+		case TCSETAW:
+			opt |= TERMIOS_WAIT;
+		case TCSETA:
+			return set_termios(real_tty, arg, opt|TERMIOS_TERMIO);
+		case TCXONC:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			switch (arg) {
+			case TCOOFF:
+				stop_tty(tty);
+				break;
+			case TCOON:
+				start_tty(tty);
+				break;
+			case TCIOFF:
+				if (STOP_CHAR(tty) != __DISABLED_CHAR)
+					tty->driver.write(tty, 0,
+							  &STOP_CHAR(tty), 1);
+				break;
+			case TCION:
+				if (START_CHAR(tty) != __DISABLED_CHAR)
+					tty->driver.write(tty, 0,
+							  &START_CHAR(tty), 1);
+				break;
+			default:
+				return -EINVAL;
+			}
+			return 0;
+		case TCFLSH:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			switch (arg) {
+			case TCIFLUSH:
+				if (tty->ldisc.flush_buffer)
+					tty->ldisc.flush_buffer(tty);
+				break;
+			case TCIOFLUSH:
+				if (tty->ldisc.flush_buffer)
+					tty->ldisc.flush_buffer(tty);
+				/* fall through */
+			case TCOFLUSH:
+				if (tty->driver.flush_buffer)
+					tty->driver.flush_buffer(tty);
+				break;
+			default:
+				return -EINVAL;
+			}
+			return 0;
+		case TIOCOUTQ:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (unsigned long));
+			if (retval)
+				return retval;
+			if (tty->driver.chars_in_buffer)
+				put_fs_long(tty->driver.chars_in_buffer(tty),
+					    (unsigned long *) arg);
+			else
+				put_fs_long(0, (unsigned long *) arg);
+			return 0;
+		case TIOCINQ:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (unsigned long));
+			if (retval)
+				return retval;
+			if (L_ICANON(tty))
+				put_fs_long(inq_canon(tty),
+					(unsigned long *) arg);
+			else
+				put_fs_long(tty->read_cnt,
+					    (unsigned long *) arg);
+			return 0;
+		case TIOCGLCKTRMIOS:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof (struct termios));
+			if (retval)
+				return retval;
+			memcpy_tofs((struct termios *) arg,
+				    real_tty->termios_locked,
+				    sizeof (struct termios));
+			return 0;
+		case TIOCSLCKTRMIOS:
+			if (!suser())
+				return -EPERM;
+			retval = verify_area(VERIFY_READ, (void *) arg,
+					     sizeof (struct termios));
+			if (retval)
+				return retval;
+			memcpy_fromfs(real_tty->termios_locked,
+				      (struct termios *) arg,
+				      sizeof (struct termios));
+			return 0;
+		case TIOCPKT:
+			if (tty->driver.type != TTY_DRIVER_TYPE_PTY ||
+			    tty->driver.subtype != PTY_TYPE_MASTER)
+				return -ENOTTY;
+			retval = verify_area(VERIFY_READ, (void *) arg,
+					     sizeof (unsigned long));
+			if (retval)
+				return retval;
+			if (get_fs_long(arg)) {
+				if (!tty->packet) {
+					tty->packet = 1;
+					tty->link->ctrl_status = 0;
+				}
+			} else
+				tty->packet = 0;
+			return 0;
+		/* These two ioctl's always return success; even if */
+		/* the driver doesn't support them. */
+		case TCSBRK: case TCSBRKP:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (!tty->driver.ioctl)
+				return 0;
+			tty->driver.ioctl(tty, file, cmd, arg);
+			return 0;
+		default:
+			return -ENOIOCTLCMD;
+		}
+}
diff -urNwbB linux/arch/arm/drivers/char/uni_hash.tbl linux.arm/arch/arm/drivers/char/uni_hash.tbl
--- linux/arch/arm/drivers/char/uni_hash.tbl	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/uni_hash.tbl	Sun Feb 11 18:15:12 1996
@@ -0,0 +1,88 @@
+/*
+ * uni_hash.tbl
+ *
+ * Do not edit this file; it was automatically generated by
+ *
+ * conmakehash cp437.uni > uni_hash.tbl
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kd.h>
+
+static u8 dfont_unicount[256] = 
+{
+	  1,   1,   1,   1,   2,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   2,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   2,   2,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   5,   1,   2,   1,   4,   1,   1,
+	  1,   5,   1,   2,   1,   1,   1,   5,
+	  1,   1,   2,   1,   1,   4,   1,   1,
+	  1,   2,   1,   1,   1,   1,   1,   2,
+	  1,   2,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   2,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  2,   2,   1,   1,   2,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   2,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   2,   1,   1,   1,   1,   2,   1,
+	  2,   1,   2,   1,   1,   2,   1,   1,
+	  1,   1,   1,   1,   1,   1,   1,   1,
+	  1,   1,   1,   1,   1,   1,   2,   1
+};
+
+static u16 dfont_unitable[295] = 
+{
+	0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x25c6, 0x2663, 0x2660,
+	0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b,
+	0x263c, 0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac,
+	0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2,
+	0x25bc, 0x0020, 0x0021, 0x0022, 0x00a8, 0x0023, 0x0024, 0x0025,
+	0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x00b8,
+	0x002d, 0x00ad, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033,
+	0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b,
+	0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x00c0, 0x00c1,
+	0x00c2, 0x00c3, 0x0042, 0x0043, 0x00a9, 0x0044, 0x0045, 0x00c8,
+	0x00ca, 0x00cb, 0x0046, 0x0047, 0x0048, 0x0049, 0x00cc, 0x00cd,
+	0x00ce, 0x00cf, 0x004a, 0x004b, 0x212a, 0x004c, 0x004d, 0x004e,
+	0x004f, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x0050, 0x0051, 0x0052,
+	0x00ae, 0x0053, 0x0054, 0x0055, 0x00d9, 0x00da, 0x00db, 0x0056,
+	0x0057, 0x0058, 0x0059, 0x00dd, 0x005a, 0x005b, 0x005c, 0x005d,
+	0x005e, 0x005f, 0xf804, 0x0060, 0x0061, 0x00e3, 0x0062, 0x0063,
+	0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b,
+	0x006c, 0x006d, 0x006e, 0x006f, 0x00f5, 0x0070, 0x0071, 0x0072,
+	0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x00d7, 0x0079,
+	0x00fd, 0x007a, 0x007b, 0x007c, 0x00a5, 0x007d, 0x007e, 0x2302,
+	0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+	0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+	0x212b, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb,
+	0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7,
+	0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa,
+	0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab,
+	0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562,
+	0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b,
+	0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e,
+	0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c,
+	0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553,
+	0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590,
+	0x2580, 0x03b1, 0x03b2, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3,
+	0x00b5, 0x03bc, 0x03c4, 0x03a6, 0x00d8, 0x0398, 0x03a9, 0x2126,
+	0x03b4, 0x221e, 0x03c6, 0x00f8, 0x03b5, 0x2229, 0x2261, 0x00b1,
+	0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219,
+	0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0xfffd, 0x00a0
+};
\ No newline at end of file
diff -urNwbB linux/arch/arm/drivers/char/vc_screen.c linux.arm/arch/arm/drivers/char/vc_screen.c
--- linux/arch/arm/drivers/char/vc_screen.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/vc_screen.c	Sat Feb 24 18:12:00 1996
@@ -0,0 +1,232 @@
+/*
+ * linux/arch/arm/drivers/char/vc_screen.c
+ *
+ * Provide access to virtual console memory.
+ * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
+ * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
+ *            [minor: N]
+ *
+ * /dev/vcsaN: idem, but including attributes, and prefixed with
+ *	the 4 bytes lines,columns,x,y (as screendump used to give)
+ *            [minor: N+128]
+ *
+ * This replaces screendump and part of selection, so that the system
+ * administrator can control access using file system permissions.
+ *
+ * aeb@cwi.nl - efter Friedas begravelse - 950211
+ *
+ * Modified by Russell King (01/01/96) [experimental + in development]
+ */
+
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include "vt_kern.h"
+#include "selection.h"
+
+#define HEADER_SIZE	4
+#define video_num_lines 75
+#define video_num_columns 100
+
+static inline int
+vcs_size(struct inode *inode)
+{
+	int size = video_num_lines * video_num_columns;
+	if (MINOR(inode->i_rdev) & 128)
+		size = 4*size + HEADER_SIZE;
+	return size;
+}
+
+static int
+vcs_lseek(struct inode *inode, struct file *file, off_t offset, int orig)
+{
+	int size = vcs_size(inode);
+
+	switch (orig) {
+		case 0:
+			file->f_pos = offset;
+			break;
+		case 1:
+			file->f_pos += offset;
+			break;
+		case 2:
+			file->f_pos = size + offset;
+			break;
+		default:
+			return -EINVAL;
+	}
+	if (file->f_pos < 0 || file->f_pos > size)
+		return -EINVAL;
+	return file->f_pos;
+}
+
+static int
+vcs_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+	unsigned long p = file->f_pos;
+	unsigned int cons = MINOR(inode->i_rdev);
+	int attr, size, read;
+	char *buf0;
+	unsigned long *org, d;
+
+	attr = (cons & 128);
+	cons = (cons & 127);
+	if (cons == 0)
+		cons = fg_console;
+	else
+		cons--;
+	if (!vc_cons_allocated(cons))
+		return -ENXIO;
+
+	size = vcs_size(inode);
+	if (count < 0 || p > size)
+		return -EINVAL;
+	if (count > size - p)
+		count = size - p;
+
+	buf0 = buf;
+	if (!attr) {
+		org = screen_pos(cons, p);
+		while (count-- > 0)
+			put_fs_byte(*org++ & 0xff, buf++);
+	} else {
+		if (p < HEADER_SIZE) {
+			char header[HEADER_SIZE];
+			header[0] = (char) video_num_lines;
+			header[1] = (char) video_num_columns;
+			getconsxy(cons, header+2);
+			while (p < HEADER_SIZE && count-- > 0)
+			    put_fs_byte(header[p++], buf++);
+		}
+		p -= HEADER_SIZE;
+		org = screen_pos(cons, p>>2);
+		if (p & 3)
+			d = *org++;
+		if ((p & 3)>0 && count-- > 0)
+			put_fs_byte(d >> 24, buf++);
+		if ((p & 3)>1 && count-- > 0)
+			put_fs_byte((d >> 16) & 0xff, buf++);
+		if ((p & 3)>2 && count-- > 0)
+			put_fs_byte((d >> 8) & 0xff, buf++);
+		while (count > 1) {
+			put_fs_long(*org++, buf);
+			buf += 4;
+			count -= 4;
+		}
+		if (count > 0)
+		{
+			d = *org;
+			put_fs_byte(d & 0xff, buf++);
+			if (count > 1)
+				put_fs_byte((d >> 8) & 0xff, buf++);
+			if (count > 2)
+				put_fs_byte((d >> 16) & 0xff, buf++);
+		}
+	}
+	read = buf - buf0;
+	file->f_pos += read;
+	return read;
+}
+
+static int
+vcs_write(struct inode *inode, struct file *file, const char *buf, int count)
+{
+	unsigned long p = file->f_pos;
+	unsigned int cons = MINOR(inode->i_rdev);
+	int viewed, attr, size, written;
+	const char *buf0;
+	unsigned long *org;
+
+	attr = (cons & 128);
+	cons = (cons & 127);
+	if (cons == 0) {
+		cons = fg_console;
+		viewed = 1;
+	} else {
+		cons--;
+		viewed = 0;
+	}
+	if (!vc_cons_allocated(cons))
+		return -ENXIO;
+
+	size = vcs_size(inode);
+	if (count < 0 || p > size)
+		return -EINVAL;
+	if (count > size - p)
+		count = size - p;
+
+	buf0 = buf;
+	if (!attr) {
+		org = screen_pos(cons, p);
+		while (count-- > 0) {
+			*org = (*org & 0xffffff00) | get_fs_byte(buf++);
+			ll_char_write (*org);
+			org++;
+		}
+	} else {
+#if 0
+		if (p < HEADER_SIZE) {
+			char header[HEADER_SIZE];
+			getconsxy(cons, header+2);
+			while (p < HEADER_SIZE && count-- > 0)
+				header[p++] = get_fs_byte(buf++);
+			if (!viewed)
+				putconsxy(cons, header+2);
+		}
+		p -= HEADER_SIZE;
+		org = screen_pos(cons, p>>2, viewed);
+		if ((p & 1) && count-- > 0) {
+			scr_writew((get_fs_byte(buf++) << 8) |
+				   (scr_readw(org) & 0xff), org);
+			org++;
+		}
+		while (count > 1) {
+			scr_writew(get_fs_word(buf), org++);
+			buf += 2;
+			count -= 2;
+		}
+		if (count > 0)
+			scr_writew((scr_readw(org) & 0xff00) |
+				   get_fs_byte(buf++), org);
+#endif
+	}
+	written = buf - buf0;
+	update_scrmem (cons, file->f_pos, written);
+	file->f_pos += written;
+	return written;
+}
+
+static int
+vcs_open(struct inode *inode, struct file *filp)
+{
+	unsigned int cons = (MINOR(inode->i_rdev) & 127);
+	if(cons && !vc_cons_allocated(cons-1))
+		return -ENXIO;
+	return 0;
+}
+
+static struct file_operations vcs_fops = {
+	vcs_lseek,	/* lseek */
+	vcs_read,	/* read */
+	vcs_write,	/* write */
+	NULL,		/* readdir */
+	NULL,		/* select */
+	NULL,		/* ioctl */
+	NULL,		/* mmap */
+	vcs_open,	/* open */
+	NULL,		/* release */
+	NULL		/* fsync */
+};
+
+int vcs_init(void)
+{
+	int error;
+
+	error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
+	if (error)
+		printk("unable to get major %d for vcs device", VCS_MAJOR);
+	return error;
+}
diff -urNwbB linux/arch/arm/drivers/char/vt.c linux.arm/arch/arm/drivers/char/vt.c
--- linux/arch/arm/drivers/char/vt.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/vt.c	Sun Mar  3 12:49:38 1996
@@ -0,0 +1,935 @@
+/*
+ *  linux/arch/arm/drivers/char/vt.c
+ *
+ *  Copyright (C) 1992 obz under the linux copyright
+ *
+ *  Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
+ *  Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
+ *  Ported to ARM - rmk92@ecs.soton.ac.uk - July 1995
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include "kbd_kern.h"
+#include "vt_kern.h"
+#include "diacr.h"
+
+
+extern struct tty_driver console_driver;
+extern int sel_cons;
+
+#define VT_IS_IN_USE(i)	(console_driver.table[i] && console_driver.table[i]->count)
+#define VT_BUSY(i)	(VT_IS_IN_USE(i) || i == fg_console || i == sel_cons)
+
+/*
+ * Console (vt and kd) routines, as defined by USL SVR4 manual, and by
+ * experimentation and study of X386 SYSV handling.
+ *
+ * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and
+ * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console,
+ * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will
+ * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to
+ * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using
+ * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing
+ * to the current console is done by the main ioctl code.
+ */
+
+struct vt_struct *vt_cons[MAX_NR_CONSOLES];
+
+asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
+
+extern int getkeycode(unsigned int scancode);
+extern int setkeycode(unsigned int scancode, unsigned int keycode);
+extern void compute_shiftstate(void);
+extern void change_console(unsigned int new_console);
+extern void complete_change_console(unsigned int new_console);
+extern int vt_waitactive(void);
+extern void do_blank_screen(int nopowersave);
+extern void do_unblank_screen(void);
+ 
+extern unsigned int keymap_count;
+
+/*
+ * routines to load custom translation table, EGA/VGA font and
+ * VGA colour palette from console.c
+ */
+extern int con_set_trans_old(unsigned char * table);
+extern int con_get_trans_old(unsigned char * table);
+extern int con_set_trans_new(unsigned short * table);
+extern int con_get_trans_new(unsigned short * table);
+extern void con_clear_unimap(struct unimapinit *ui);
+extern int con_set_unimap(ushort ct, struct unipair *list);
+extern int con_get_unimap(ushort ct, ushort *uct, struct unipair *list);
+extern void con_set_default_unimap(void);
+extern int con_set_font(char * fontmap);
+extern int con_get_font(char * fontmap);
+
+/*
+ * these are the valid i/o ports we're allowed to change. they map all the
+ * video ports
+ */
+#define GPFIRST 0x3b4
+#define GPLAST 0x3df
+#define GPNUM (GPLAST - GPFIRST + 1)
+
+/*
+ * Generates sound of some count for some number of clock ticks
+ * [count = 1193180 / frequency]
+ *
+ * If freq is 0, will turn off sound, else will turn it on for that time.
+ * If msec is 0, will return immediately, else will sleep for msec time, then
+ * turn sound off.
+ *
+ * We use the BEEP_TIMER vector since we're using the same method to
+ * generate sound, and we'll overwrite any beep in progress. That may
+ * be something to fix later, if we like.
+ *
+ * We also return immediately, which is what was implied within the X
+ * comments - KDMKTONE doesn't put the process to sleep.
+ */
+static inline void
+kd_mksound2(unsigned int count, unsigned int vol, unsigned int ticks)
+{
+    extern void mk_beep (unsigned int count, unsigned int vol, unsigned int ticks);
+#ifdef CONFIG_SOUND
+    mk_beep(count, vol, ticks);
+#endif
+}
+
+void
+kd_mksound(unsigned int count, unsigned int ticks)
+{
+    kd_mksound2(count, 72, ticks);
+}
+
+/*
+ * We handle the console-specific ioctl's here.  We allow the
+ * capability to modify any console, not just the fg_console.
+ */
+int vt_ioctl(struct tty_struct *tty, struct file * file,
+	     unsigned int cmd, unsigned long arg)
+{
+	int i, perm;
+	unsigned int console;
+	unsigned char ucval;
+	struct kbd_struct * kbd;
+	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+
+	console = vt->vc_num;
+
+	if (!vc_cons_allocated(console)) 	/* impossible? */
+		return -ENOIOCTLCMD;
+
+	/*
+	 * To have permissions to do most of the vt ioctls, we either have
+	 * to be the owner of the tty, or super-user.
+	 */
+	perm = 0;
+	if (current->tty == tty || suser())
+		perm = 1;
+
+	kbd = kbd_table + console;
+	switch (cmd) {
+	case KIOCSOUND:
+		if (!perm)
+			return -EPERM;
+		kd_mksound((unsigned int)arg, 5);
+		return 0;
+
+	case KDMKTONE:
+		if (!perm)
+			return -EPERM;
+	{
+		unsigned int freq  = ((unsigned long *)arg)[0];
+		unsigned int vol   = ((unsigned long *)arg)[1];
+		unsigned int ticks = HZ * (((unsigned long *)arg)[2] & 0xffff) / 1000;
+
+		/*
+		 * Generate the tone for the appropriate number of ticks.
+		 * If the time is zero, turn off sound ourselves.
+		 */
+		kd_mksound2(freq, vol, ticks);
+		return 0;
+	}
+
+	case KDGKBTYPE:
+		/*
+		 * this is naive.
+		 */
+		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
+		if (!i)
+			put_user(KB_101, (char *) arg);
+		return i;
+
+	case KDADDIO:
+	case KDDELIO:
+		/*
+		 * KDADDIO and KDDELIO may be able to add ports beyond what
+		 * we reject here, but to be safe...
+		 */
+		if (arg < GPFIRST || arg > GPLAST)
+			return -EINVAL;
+		return sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
+
+	case KDENABIO:
+	case KDDISABIO:
+		return sys_ioperm(GPFIRST, GPNUM,
+				  (cmd == KDENABIO)) ? -ENXIO : 0;
+
+	case KDSETMODE:
+		/*
+		 * currently, setting the mode from KD_TEXT to KD_GRAPHICS
+		 * doesn't do a whole lot. i'm not sure if it should do any
+		 * restoration of modes or what...
+		 */
+		if (!perm)
+			return -EPERM;
+		switch (arg) {
+		case KD_GRAPHICS:
+			break;
+		case KD_TEXT0:
+		case KD_TEXT1:
+			arg = KD_TEXT;
+		case KD_TEXT:
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (vt_cons[console]->vc_mode == (unsigned char) arg)
+			return 0;
+		vt_cons[console]->vc_mode = (unsigned char) arg;
+		if (console != fg_console)
+			return 0;
+		/*
+		 * explicitly blank/unblank the screen if switching modes
+		 */
+		if (arg == KD_TEXT)
+			do_unblank_screen();
+		else
+			do_blank_screen(1);
+		return 0;
+
+	case KDGETMODE:
+		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+		if (!i)
+			put_user(vt_cons[console]->vc_mode, (unsigned long *) arg);
+		return i;
+
+	case KDMAPDISP:
+	case KDUNMAPDISP:
+		/*
+		 * these work like a combination of mmap and KDENABIO.
+		 * this could be easily finished.
+		 */
+		return -EINVAL;
+
+	case KDSKBMODE:
+		if (!perm)
+			return -EPERM;
+		switch(arg) {
+		  case K_RAW:
+			kbd->kbdmode = VC_RAW;
+			break;
+		  case K_MEDIUMRAW:
+			kbd->kbdmode = VC_MEDIUMRAW;
+			break;
+		  case K_XLATE:
+			kbd->kbdmode = VC_XLATE;
+			compute_shiftstate();
+			break;
+		  case K_UNICODE:
+			kbd->kbdmode = VC_UNICODE;
+			compute_shiftstate();
+			break;
+		  default:
+			return -EINVAL;
+		}
+		if (tty->ldisc.flush_buffer)
+			tty->ldisc.flush_buffer(tty);
+		return 0;
+
+	case KDGKBMODE:
+		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+		if (!i) {
+			ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW :
+				 (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
+				 (kbd->kbdmode == VC_UNICODE) ? K_UNICODE :
+				 K_XLATE);
+			put_user(ucval, (int *) arg);
+		}
+		return i;
+
+	/* this could be folded into KDSKBMODE, but for compatibility
+	   reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
+	case KDSKBMETA:
+		switch(arg) {
+		  case K_METABIT:
+			clr_vc_kbd_mode(kbd, VC_META);
+			break;
+		  case K_ESCPREFIX:
+			set_vc_kbd_mode(kbd, VC_META);
+			break;
+		  default:
+			return -EINVAL;
+		}
+		return 0;
+
+	case KDGKBMETA:
+		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+		if (!i) {
+			ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX :
+				 K_METABIT);
+			put_user(ucval, (int *) arg);
+		}
+		return i;
+
+	case KDGETKEYCODE:
+	{
+		struct kbkeycode * const a = (struct kbkeycode *)arg;
+		unsigned int sc;
+		int kc;
+
+		i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbkeycode));
+		if (i)
+			return i;
+		sc = get_user(&a->scancode);
+		kc = getkeycode(sc);
+		if (kc < 0)
+			return kc;
+		put_user(kc, &a->keycode);
+		return 0;
+	}
+
+	case KDSETKEYCODE:
+	{
+		struct kbkeycode * const a = (struct kbkeycode *)arg;
+		unsigned int sc, kc;
+
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
+		if (i)
+			return i;
+		sc = get_user(&a->scancode);
+		kc = get_user(&a->keycode);
+		return setkeycode(sc, kc);
+	}
+
+	case KDGKBENT:
+	{
+		struct kbentry * const a = (struct kbentry *)arg;
+		ushort *key_map, val;
+		u_char s;
+
+		i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbentry));
+		if (i)
+			return i;
+		if ((i = get_user(&a->kb_index)) >= NR_KEYS)
+			return -EINVAL;
+		if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
+			return -EINVAL;
+		key_map = key_maps[s];
+		if (key_map) {
+		    val = U(key_map[i]);
+		    if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+			val = K_HOLE;
+		} else
+		    val = (i ? K_HOLE : K_NOSUCHMAP);
+		put_user(val, &a->kb_value);
+		return 0;
+	}
+
+	case KDSKBENT:
+	{
+		const struct kbentry * a = (struct kbentry *)arg;
+		ushort *key_map;
+		u_char s;
+		u_short v, ov;
+
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbentry));
+		if (i)
+			return i;
+		if ((i = get_user(&a->kb_index)) >= NR_KEYS)
+			return -EINVAL;
+		if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
+			return -EINVAL;
+		v = get_user(&a->kb_value);
+		if (!i && v == K_NOSUCHMAP) {
+			/* disallocate map */
+			key_map = key_maps[s];
+			if (s && key_map) {
+			    key_maps[s] = 0;
+			    if (key_map[0] == U(K_ALLOCATED)) {
+				kfree_s(key_map, sizeof(plain_map));
+				keymap_count--;
+			    }
+			}
+			return 0;
+		}
+
+		if (KTYP(v) < NR_TYPES) {
+		    if (KVAL(v) > max_vals[KTYP(v)])
+			return -EINVAL;
+		} else
+		    if (kbd->kbdmode != VC_UNICODE)
+			return -EINVAL;
+
+		/* assignment to entry 0 only tests validity of args */
+		if (!i)
+			return 0;
+
+		if (!(key_map = key_maps[s])) {
+			int j;
+			
+			if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
+				return -EPERM;
+
+			key_map = (ushort *) kmalloc(sizeof(plain_map),
+						     GFP_KERNEL);
+			if (!key_map)
+				return -ENOMEM;
+			key_maps[s] = key_map;
+			key_map[0] = U(K_ALLOCATED);
+			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 (((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;
+	}
+
+	case KDGKBSENT:
+	{
+		struct kbsentry *a = (struct kbsentry *)arg;
+		char *p;
+		u_char *q;
+		int sz;
+
+		i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
+		if (i)
+			return i;
+		if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
+			return -EINVAL;
+		sz = sizeof(a->kb_string) - 1; /* sz should have been
+						  a struct member */
+		q = a->kb_string;
+		p = func_table[i];
+		if(p)
+			for ( ; *p && sz; p++, sz--)
+				put_user(*p, q++);
+		put_user('\0', q);
+		return ((p && *p) ? -EOVERFLOW : 0);
+	}
+
+	case KDSKBSENT:
+	{
+		struct kbsentry * const a = (struct kbsentry *)arg;
+		int delta;
+		char *first_free, *fj, *fnw;
+		int j, k, sz;
+		u_char *p;
+		char *q;
+
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
+		if (i)
+			return i;
+		if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC)
+			return -EINVAL;
+		q = func_table[i];
+
+		first_free = funcbufptr + (funcbufsize - funcbufleft);
+		for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
+		if (j < MAX_NR_FUNC)
+			fj = func_table[j];
+		else
+			fj = first_free;
+
+		delta = (q ? -strlen(q) : 1);
+		sz = sizeof(a->kb_string); 	/* sz should have been
+						   a struct member */
+		for (p = a->kb_string; get_user(p) && sz; p++,sz--)
+			delta++;
+		if (!sz)
+			return -EOVERFLOW;
+		if (delta <= funcbufleft) { 	/* it fits in current buf */
+		    if (j < MAX_NR_FUNC) {
+			memmove(fj + delta, fj, first_free - fj);
+			for (k = j; k < MAX_NR_FUNC; k++)
+			    if (func_table[k])
+				func_table[k] += delta;
+		    }
+		    if (!q)
+		      func_table[i] = fj;
+		    funcbufleft -= delta;
+		} else {			/* allocate a larger buffer */
+		    sz = 256;
+		    while (sz < funcbufsize - funcbufleft + delta)
+		      sz <<= 1;
+		    fnw = (char *) kmalloc(sz, GFP_KERNEL);
+		    if(!fnw)
+		      return -ENOMEM;
+
+		    if (!q)
+		      func_table[i] = fj;
+		    if (fj > funcbufptr)
+			memmove(fnw, funcbufptr, fj - funcbufptr);
+		    for (k = 0; k < j; k++)
+		      if (func_table[k])
+			func_table[k] = fnw + (func_table[k] - funcbufptr);
+
+		    if (first_free > fj) {
+			memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
+			for (k = j; k < MAX_NR_FUNC; k++)
+			  if (func_table[k])
+			    func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
+		    }
+		    if (funcbufptr != func_buf)
+		      kfree_s(funcbufptr, funcbufsize);
+		    funcbufptr = fnw;
+		    funcbufleft = funcbufleft - delta + sz - funcbufsize;
+		    funcbufsize = sz;
+		}
+		for (p = a->kb_string, q = func_table[i]; ; p++, q++)
+			if (!(*q = get_user(p)))
+				break;
+		return 0;
+	}
+
+	case KDGKBDIACR:
+	{
+		struct kbdiacrs *a = (struct kbdiacrs *)arg;
+
+		i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
+		if (i)
+			return i;
+		put_user(accent_table_size, &a->kb_cnt);
+		memcpy_tofs(a->kbdiacr, accent_table,
+			    accent_table_size*sizeof(struct kbdiacr));
+		return 0;
+	}
+
+	case KDSKBDIACR:
+	{
+		struct kbdiacrs *a = (struct kbdiacrs *)arg;
+		unsigned int ct;
+
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
+		if (i)
+			return i;
+		ct = get_user(&a->kb_cnt);
+		if (ct >= MAX_DIACR)
+			return -EINVAL;
+		accent_table_size = ct;
+		memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
+		return 0;
+	}
+
+	/* the ioctls below read/set the flags usually shown in the leds */
+	/* don't use them - they will go away without warning */
+	case KDGKBLED:
+		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
+		if (i)
+			return i;
+		put_user(kbd->ledflagstate |
+			    (kbd->default_ledflagstate << 4), (char *) arg);
+		return 0;
+
+	case KDSKBLED:
+		if (!perm)
+			return -EPERM;
+		if (arg & ~0x77)
+			return -EINVAL;
+		kbd->ledflagstate = (arg & 7);
+		kbd->default_ledflagstate = ((arg >> 4) & 7);
+		set_leds();
+		return 0;
+
+	/* the ioctls below only set the lights, not the functions */
+	/* for those, see KDGKBLED and KDSKBLED above */
+	case KDGETLED:
+		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
+		if (i)
+			return i;
+		put_user(getledstate(), (char *) arg);
+		return 0;
+
+	case KDSETLED:
+		if (!perm)
+			return -EPERM;
+		setledstate(kbd, arg);
+		return 0;
+
+	/*
+	 * A process can indicate its willingness to accept signals
+	 * generated by pressing an appropriate key combination.
+	 * Thus, one can have a daemon that e.g. spawns a new console
+	 * upon a keypess and then changes to it.
+	 * Probably init should be changed to do this (and have a
+	 * field ks (`keyboard signal') in inittab describing the
+	 * desired acion), so that the number of background daemons
+	 * does not increase.
+	 */
+	case KDSIGACCEPT:
+	{
+		extern int spawnpid, spawnsig;
+		if (!perm)
+		  return -EPERM;
+		if (arg < 1 || arg > NSIG || arg == SIGKILL)
+		  return -EINVAL;
+		spawnpid = current->pid;
+		spawnsig = arg;
+		return 0;
+	}
+
+	case VT_SETMODE:
+	{
+		struct vt_mode *vtmode = (struct vt_mode *)arg;
+		char mode;
+
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
+		if (i)
+			return i;
+		mode = get_user(&vtmode->mode);
+		if (mode != VT_AUTO && mode != VT_PROCESS)
+			return -EINVAL;
+		vt_cons[console]->vt_mode.mode = mode;
+		vt_cons[console]->vt_mode.waitv = get_user(&vtmode->waitv);
+		vt_cons[console]->vt_mode.relsig = get_user(&vtmode->relsig);
+		vt_cons[console]->vt_mode.acqsig = get_user(&vtmode->acqsig);
+		/* the frsig is ignored, so we set it to 0 */
+		vt_cons[console]->vt_mode.frsig = 0;
+		vt_cons[console]->vt_pid = current->pid;
+		vt_cons[console]->vt_newvt = 0;
+		return 0;
+	}
+
+	case VT_GETMODE:
+	{
+		struct vt_mode *vtmode = (struct vt_mode *)arg;
+
+		i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct vt_mode));
+		if (i)
+			return i;
+		put_user(vt_cons[console]->vt_mode.mode, &vtmode->mode);
+		put_user(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
+		put_user(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
+		put_user(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
+		put_user(vt_cons[console]->vt_mode.frsig, &vtmode->frsig);
+		return 0;
+	}
+
+	/*
+	 * Returns global vt state. Note that VT 0 is always open, since
+	 * it's an alias for the current VT, and people can't use it here.
+	 * We cannot return state for more than 16 VTs, since v_state is short.
+	 */
+	case VT_GETSTATE:
+	{
+		struct vt_stat *vtstat = (struct vt_stat *)arg;
+		unsigned short state, mask;
+
+		i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat));
+		if (i)
+			return i;
+		put_user(fg_console + 1, &vtstat->v_active);
+		state = 1;	/* /dev/tty0 is always open */
+		for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
+			if (VT_IS_IN_USE(i))
+				state |= mask;
+		put_user(state, &vtstat->v_state);
+		return 0;
+	}
+
+	/*
+	 * Returns the first available (non-opened) console.
+	 */
+	case VT_OPENQRY:
+		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+		if (i)
+			return i;
+		for (i = 0; i < MAX_NR_CONSOLES; ++i)
+			if (! VT_IS_IN_USE(i))
+				break;
+		put_user(i < MAX_NR_CONSOLES ? (i+1) : -1, (int *) arg);
+		return 0;
+
+	/*
+	 * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
+	 * with num >= 1 (switches to vt 0, our console, are not allowed, just
+	 * to preserve sanity).
+	 */
+	case VT_ACTIVATE:
+		if (!perm)
+			return -EPERM;
+		if (arg == 0 || arg > MAX_NR_CONSOLES)
+			return -ENXIO;
+		arg--;
+		i = vc_allocate(arg);
+		if (i)
+			return i;
+		change_console(arg);
+		return 0;
+
+	/*
+	 * wait until the specified VT has been activated
+	 */
+	case VT_WAITACTIVE:
+		if (!perm)
+			return -EPERM;
+		if (arg == 0 || arg > MAX_NR_CONSOLES)
+			return -ENXIO;
+		arg--;
+		while (fg_console != arg)
+		{
+			if (vt_waitactive() < 0)
+				return -EINTR;
+		}
+		return 0;
+
+	/*
+	 * If a vt is under process control, the kernel will not switch to it
+	 * immediately, but postpone the operation until the process calls this
+	 * ioctl, allowing the switch to complete.
+	 *
+	 * According to the X sources this is the behavior:
+	 *	0:	pending switch-from not OK
+	 *	1:	pending switch-from OK
+	 *	2:	completed switch-to OK
+	 */
+	case VT_RELDISP:
+		if (!perm)
+			return -EPERM;
+		if (vt_cons[console]->vt_mode.mode != VT_PROCESS)
+			return -EINVAL;
+
+		/*
+		 * Switching-from response
+		 */
+		if (vt_cons[console]->vt_newvt >= 0)
+		{
+			if (arg == 0)
+				/*
+				 * Switch disallowed, so forget we were trying
+				 * to do it.
+				 */
+				vt_cons[console]->vt_newvt = -1;
+
+			else
+			{
+				/*
+				 * The current vt has been released, so
+				 * complete the switch.
+				 */
+				int newvt = vt_cons[console]->vt_newvt;
+				vt_cons[console]->vt_newvt = -1;
+				i = vc_allocate(newvt);
+				if (i)
+					return i;
+				complete_change_console(newvt);
+			}
+		}
+
+		/*
+		 * Switched-to response
+		 */
+		else
+		{
+			/*
+			 * If it's just an ACK, ignore it
+			 */
+			if (arg != VT_ACKACQ)
+				return -EINVAL;
+		}
+
+		return 0;
+
+	 /*
+	  * Disallocate memory associated to VT (but leave VT1)
+	  */
+	 case VT_DISALLOCATE:
+		if (arg > MAX_NR_CONSOLES)
+			return -ENXIO;
+		if (arg == 0) {
+		    /* disallocate all unused consoles, but leave 0 */
+		    for (i = 1; i<MAX_NR_CONSOLES; i++)
+		      if (! VT_BUSY(i))
+			  vc_disallocate(i);
+		} else {
+		    /* disallocate a single console, if possible */
+		    arg--;
+		    if (VT_BUSY(arg))
+		      return -EBUSY;
+		    if (arg)				/* leave 0 */
+			vc_disallocate(arg);
+		}
+		return 0;
+
+	case VT_RESIZE:
+	{
+		struct vt_sizes *vtsizes = (struct vt_sizes *) arg;
+		ushort ll,cc;
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
+		if (i)
+			return i;
+		ll = get_user(&vtsizes->v_rows);
+		cc = get_user(&vtsizes->v_cols);
+		return vc_resize(ll, cc);
+	}
+	
+	case VT_GETPALETTE:
+	{
+		unsigned long pix, num, *val;
+		extern int palette_getentries (unsigned int, unsigned long,
+			unsigned long, unsigned long);
+		
+	        i = verify_area(VERIFY_WRITE, (void *)arg, 12);
+	        if (i)
+	        	return i;
+	        pix = get_fs_long((unsigned long *)arg);
+	        num = get_fs_long((unsigned long *)(arg + 4));
+	        val = (unsigned long *)get_fs_long((unsigned long *)(arg + 8));
+		return palette_getentries(console, pix, num, val);
+	}
+
+	case VT_SETPALETTE:
+	{
+		unsigned long pix, num, *val;
+		extern int palette_setentries (unsigned int, unsigned long,
+			unsigned long, unsigned long);
+
+	        i = verify_area(VERIFY_READ, (void *)arg, 12);
+	        if (i)
+	        	return i;
+
+	        pix = get_fs_long((unsigned long *)arg);
+	        num = get_fs_long((unsigned long *)(arg + 4));
+	        val = (unsigned long *)get_fs_long((unsigned long *)(arg + 8));
+		return palette_setentries(console, pix, num, val);
+	}
+
+	case VT_GETSCRINFO: {
+		extern int console_getparams (unsigned int, unsigned long arg);
+		i = verify_area(VERIFY_WRITE, (void *)arg, 20);
+	        if (i)
+	        	return i;
+		return console_getparams(console, arg);
+	}
+
+	case PIO_FONT:
+		if (!perm)
+			return -EPERM;
+		if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+			return -EINVAL;
+		return con_set_font((char *)arg);
+		/* con_set_font() defined in console.c */
+
+	case GIO_FONT:
+		if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+			return -EINVAL;
+		return con_get_font((char *)arg);
+		/* con_get_font() defined in console.c */
+
+	case PIO_SCRNMAP:
+		if (!perm)
+			return -EPERM;
+		return con_set_trans_old((char *)arg);
+
+	case GIO_SCRNMAP:
+		return con_get_trans_old((char *)arg);
+
+	case PIO_UNISCRNMAP:
+		if (!perm)
+			return -EPERM;
+		return con_set_trans_new((short *)arg);
+
+	case GIO_UNISCRNMAP:
+		return con_get_trans_new((short *)arg);
+
+	case PIO_UNIMAPCLR:
+	      {	struct unimapinit ui;
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapinit));
+		if (i)
+		  return i;
+		memcpy_fromfs(&ui, (void *)arg, sizeof(struct unimapinit));
+		con_clear_unimap(&ui);
+		return 0;
+	      }
+
+	case PIO_UNIMAP:
+	      {	struct unimapdesc *ud;
+		u_short ct;
+		struct unipair *list;
+
+		if (!perm)
+			return -EPERM;
+		i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
+		if (i == 0) {
+		    ud = (struct unimapdesc *) arg;
+		    ct = get_fs_word(&ud->entry_ct);
+		    list = (struct unipair *) get_fs_long(&ud->entries);
+		    i = verify_area(VERIFY_READ, (void *) list,
+		    			ct*sizeof(struct unipair));
+		}
+		if (i)
+		  return i;
+		return con_set_unimap(ct, list);
+	      }
+
+	case GIO_UNIMAP:
+	      {	struct unimapdesc *ud;
+		u_short ct;
+		struct unipair *list;
+
+		i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
+		if (i == 0) {
+		    ud = (struct unimapdesc *) arg;
+		    ct = get_fs_word(&ud->entry_ct);
+		    list = (struct unipair *) get_fs_long(&ud->entries);
+		    if (ct)
+		      i = verify_area(VERIFY_WRITE, (void *) list,
+		      			ct*sizeof(struct unipair));
+		}
+		if (i)
+		  return i;
+		return con_get_unimap(ct, &(ud->entry_ct), list);
+	      }
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
diff -urNwbB linux/arch/arm/drivers/char/vt_kern.h linux.arm/arch/arm/drivers/char/vt_kern.h
--- linux/arch/arm/drivers/char/vt_kern.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/vt_kern.h	Sun Feb 11 19:37:27 1996
@@ -0,0 +1,29 @@
+#ifndef _VT_KERN_H
+#define _VT_KERN_H
+
+/*
+ * this really is an extension of the vc_cons structure in console.c, but
+ * with information needed by the vt package
+ */
+
+#include <linux/vt.h>
+
+extern struct vt_struct {
+	int vc_num;				/* The console number */
+	unsigned char	vc_mode;		/* KD_TEXT, ... */
+	unsigned char	vc_kbdraw;
+	unsigned char	vc_kbde0;
+	unsigned char   vc_kbdleds;
+	struct vt_mode	vt_mode;
+	int		vt_pid;
+	int		vt_newvt;
+	struct wait_queue *paste_wait;
+} *vt_cons[MAX_NR_CONSOLES];
+
+void kd_mksound(unsigned int count, unsigned int ticks);
+int vc_allocate(unsigned int console);
+int vc_cons_allocated(unsigned int console);
+int vc_resize(unsigned long lines, unsigned long cols);
+void vc_disallocate(unsigned int console);
+
+#endif /* _VT_KERN_H */
diff -urNwbB linux/arch/arm/drivers/net/Makefile linux.arm/arch/arm/drivers/net/Makefile
--- linux/arch/arm/drivers/net/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/Makefile	Sat Feb 24 21:37:51 1996
@@ -0,0 +1,155 @@
+# File: drivers/net/Makefile
+#
+# Makefile for the Linux network (ethercard) device drivers.
+#
+
+all: links first_rule
+
+ifeq (${CFLAGS},)
+CFLAGS = -D__KERNEL__ \
+	-Wall -Wstrict-prototypes -I. -I../../../../include \
+	-O2 -fomit-frame-pointer
+
+include ../../../../.config
+
+#TOPDIR = ../../../..
+endif
+
+L_TARGET := net.a
+L_OBJS   := Space.o auto_irq.o net_init.o loopback.o
+M_OBJS   :=
+SYMTAB_OBJS =
+MOD_LIST_NAME := NET_MODULES
+
+# Need these to keep track of whether the SLHC module should
+# really go in the kernel or a module
+CONFIG_SLHC_BUILTIN :=
+CONFIG_SLHC_MODULE  :=
+
+ifeq ($(CONFIG_ETHER1),y)
+L_OBJS += ether1.o
+else
+  ifeq ($(CONFIG_ETHER1),m)
+  M_OBJS += ether1.o
+  endif
+endif
+
+ifeq ($(CONFIG_ETHER3),y)
+L_OBJS += ether3.o
+else
+  ifeq ($(CONFIG_ETHER3),m)
+  M_OBJS += ether3.o
+  endif
+endif
+
+ifeq ($(CONFIG_IPIP),y)
+L_OBJS += tunnel.o
+else
+  ifeq ($(CONFIG_IPIP),m)
+  M_OBJS += tunnel.o
+  endif
+endif
+
+ifeq ($(CONFIG_PLIP),y)
+L_OBJS += plip.o
+else
+  ifeq ($(CONFIG_PLIP),m)
+  M_OBJS += plip.o
+  endif
+endif
+
+ifeq ($(CONFIG_PPP),y)
+L_OBJS += ppp.o
+SYMTAB_OBJS += ppp.o
+CONFIG_SLHC_BUILTIN = y
+else
+  ifeq ($(CONFIG_PPP),m)
+  M_OBJS += ppp.o
+  SYMTAB_OBJS += ppp.o
+  CONFIG_SLHC_MODULE = y
+  endif
+endif
+
+ifneq ($(CONFIG_PPP),n)
+  M_OBJS += bsd_comp.o
+endif
+
+ifeq ($(CONFIG_SLIP),y)
+L_OBJS += slip.o
+CONFIG_SLHC_BUILTIN = y
+else
+  ifeq ($(CONFIG_SLIP),m)
+  M_OBJS += slip.o
+  CONFIG_SLHC_MODULE = y
+  endif
+endif
+
+ifeq ($(CONFIG_DUMMY),y)
+L_OBJS += dummy.o
+else
+  ifeq ($(CONFIG_DUMMY),m)
+  L_OBJS += dummy.o
+  endif
+endif
+
+# If anything built-in uses slhc, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+ifdef CONFIG_SLHC_BUILTIN
+L_OBJS += slhc.o
+else
+  ifdef CONFIG_SLHC_MODULE
+  M_OBJS += slhc.o
+  endif
+endif
+
+ifeq ($(CONFIG_EQUALIZER),y)
+L_OBJS += eql.o
+else
+  ifeq ($(CONFIG_EQUALIZER),m)
+  M_OBJS += eql.o
+  endif
+endif
+
+fastdep: links
+
+ifdef CONFIG_MODVERSIONS
+$(L_TARGET): $(SYMTAB_OBJS:.o=.ver)
+dep: $(SYMTAB_OBJS:.o=.ver)
+fastdep: $(SYMTAB_OBJS:.o=.ver)
+endif
+
+include $(TOPDIR)/Rules.make
+
+include $(TOPDIR)/versions.mk
+
+clean:
+	rm -f core *.o *.a *.s
+
+Space.o: Space.c ../../../../include/linux/autoconf.h
+	$(CC) $(CFLAGS) $(OPTS) -c $<
+
+net_init.o: ../../../../include/linux/autoconf.h
+
+plip.o:	plip.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
+
+slip.o:	slip.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
+
+dummy.o: dummy.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
+
+LK = README.eql README.multicast README.tunnel README1.PLIP README2.PLIP bsd_comp.c dummy.c \
+	eql.c loopback.c net_init.c slhc.c slhc.h slip.c slip.h tunnel.c
+
+links:
+	-@for f in $(LK); do \
+		ln -s ../../../../drivers/net/$$f .; \
+	done
+	touch links
+
+LINKCLEAN:
+	-@for f in $(LK); do \
+		if [ -L $$f ]; then rm $$f; fi; \
+	done
+	rm -f links
diff -urNwbB linux/arch/arm/drivers/net/Space.c linux.arm/arch/arm/drivers/net/Space.c
--- linux/arch/arm/drivers/net/Space.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/Space.c	Tue Feb 13 23:09:15 1996
@@ -0,0 +1,226 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Holds initial configuration information for devices.
+ *
+ * NOTE:	This file is a nice idea, but its current format does not work
+ *		well for drivers that support multiple units, like the SLIP
+ *		driver.  We should actually have only one pointer to a driver
+ *		here, with the driver knowing how many units it supports.
+ *		Currently, the SLIP driver abuses the "base_addr" integer
+ *		field of the 'device' structure to store the unit number...
+ *		-FvK
+ *
+ * Version:	@(#)Space.c	1.0.7	08/12/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald J. Becker, <becker@super.org>
+ *
+ *		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.
+ */
+#include <linux/config.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+
+/* The first device defaults to I/O base '0', which means autoprobe. */
+#ifndef ETH0_ADDR
+# define ETH0_ADDR 0
+#endif
+#ifndef ETH0_IRQ
+# define ETH0_IRQ 0
+#endif
+/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20),
+   which means "don't probe".  These entries exist to only to provide empty
+   slots which may be enabled at boot-time. */
+
+#define	NEXT_DEV	NULL
+
+
+/* A unified ethernet device probe.  This is the easiest way to have every
+   ethernet adaptor have the name "eth[0123...]".
+   */
+extern int ether1_probe (struct device *dev);
+extern int ether3_probe (struct device *dev);
+
+static int
+ethif_probe(struct device *dev)
+{
+    u_long base_addr = dev->base_addr;
+
+    if ((base_addr == 0xffe0)  ||  (base_addr == 1))
+	return 1;		/* ENXIO */
+
+    if (1
+#ifdef CONFIG_ETHER1
+	&& ether1_probe (dev)
+#endif
+#ifdef CONFIG_ETHER3
+        && ether3_probe (dev)
+#endif
+	&& 1 ) {
+	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
+    }
+    return 0;
+}
+
+static struct device eth3_dev = {
+	"eth3",
+	0x0, 0x0, 0x0, 0x0,
+	0xffe0 /* I/O base*/, 0, 0, 0, 0,
+	NEXT_DEV, ethif_probe,
+};
+
+static struct device eth2_dev = {
+	"eth2",
+	0x0, 0x0, 0x0, 0x0,
+	0xffe0 /* I/O base*/, 0, 0, 0, 0,
+	&eth3_dev, ethif_probe,
+};
+
+static struct device eth1_dev = {
+	"eth1",
+	0x0, 0x0, 0x0, 0x0,
+	0xffe0 /* I/O base*/, 0, 0, 0, 0,
+	&eth2_dev, ethif_probe,
+};
+
+static struct device eth0_dev = {
+	"eth0",
+	0x0, 0x0, 0x0, 0x0,
+	ETH0_ADDR, ETH0_IRQ, 0, 0, 0,
+	&eth1_dev, ethif_probe,
+};
+
+#undef  NEXT_DEV
+#define NEXT_DEV (&eth0_dev)
+
+#if defined(PLIP) || defined(CONFIG_PLIP)
+extern int plip_init(struct device *);
+
+static struct device plip2_dev = {
+	"plip2",
+	0x0, 0x0, 0x0, 0x0,
+	0x278, 2, 0, 0, 0,
+	NEXT_DEV, plip_init,
+};
+
+static struct device plip1_dev = {
+	"plip1",
+	0x0, 0x0, 0x0, 0x0,
+	0x378, 7, 0, 0, 0,
+	&plip2_dev, plip_init,
+};
+
+static struct device plip0_dev = {
+	"plip0",
+	0x0, 0x0, 0x0, 0x0,
+	0x3BC, 5, 0, 0, 0,
+	&plip1_dev, plip_init,
+};
+
+#undef  NEXT_DEV
+#define NEXT_DEV (&plip0_dev)
+#endif  /* PLIP */
+
+#if defined(SLIP) || defined(CONFIG_SLIP)
+	/* To be exact, this node just hooks the initialization
+	   routines to the device structures.			*/
+extern int slip_init_ctrl_dev(struct device *);
+
+static struct device slip_bootstrap = {
+	"slip_proto",
+	0x0, 0x0, 0x0, 0x0,
+	0, 0, 0, 0, 0,
+	NEXT_DEV, slip_init_ctrl_dev,
+};
+
+#undef  NEXT_DEV
+#define NEXT_DEV (&slip_bootstrap)
+#endif /* SLIP */
+  
+#if defined(CONFIG_PPP)
+extern int ppp_init(struct device *);
+
+static struct device ppp_bootstrap = {
+	"ppp_proto",
+	0x0, 0x0, 0x0, 0x0,
+	0, 0, 0, 0, 0,
+	NEXT_DEV, ppp_init,
+};
+
+#undef  NEXT_DEV
+#define NEXT_DEV (&ppp_bootstrap)
+#endif   /* PPP */
+
+#ifdef CONFIG_DUMMY
+extern int dummy_init(struct device *dev);
+
+static struct device dummy_dev = {
+	"dummy",
+	0x0, 0x0, 0x0, 0x0,
+	0, 0, 0, 0, 0,
+	NEXT_DEV, dummy_init,
+};
+	
+#undef	NEXT_DEV
+#define	NEXT_DEV	(&dummy_dev)
+#endif
+
+#ifdef CONFIG_EQUALIZER
+extern int eql_init(struct device *dev);
+
+static struct device eql_dev = {
+	"eql",
+	0x0, 0x0, 0x0, 0x0,
+	0, 0, 0, 0, 0,
+	NEXT_DEV, eql_init,
+};
+
+#undef  NEXT_DEV
+#define NEXT_DEV (&eql_dev)
+#endif
+
+#ifdef CONFIG_NET_IPIP
+#ifdef CONFIG_IP_FORWARD
+extern int tunnel_init (struct device *dev);
+
+static struct device tunnel_dev1 = {
+	"tunl1",
+  	0x0, 0x0, 0x0, 0x0,
+	0, 0, 0, 0, 0,
+	NEXT_DEV, tunnel_init,
+};
+
+static struct device tunnel_dev0 = {
+	"tunl0",
+	0x0, 0x0, 0x0, 0x0,
+	0, 0, 0, 0, 0,
+	&tunnel_dev1, tunnel_init,
+};
+
+#undef  NEXT_DEV
+#define NEXT_DEV (&tunnel_dev0)
+#endif
+#endif
+
+extern int loopback_init(struct device *dev);
+struct device loopback_dev = {
+	"lo",			/* Software Loopback interface		*/
+	0x0,			/* recv memory end			*/
+	0x0,			/* recv memory start			*/
+	0x0,			/* memory end				*/
+	0x0,			/* memory start				*/
+	0,			/* base I/O address			*/
+	0,			/* IRQ					*/
+	0, 0, 0,		/* flags				*/
+	NEXT_DEV,		/* next device				*/
+	loopback_init		/* loopback_init should set up the rest	*/
+};
+
+struct device *dev_base = &loopback_dev;
diff -urNwbB linux/arch/arm/drivers/net/auto_irq.c linux.arm/arch/arm/drivers/net/auto_irq.c
--- linux/arch/arm/drivers/net/auto_irq.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/auto_irq.c	Sun Feb 11 09:32:25 1996
@@ -0,0 +1,122 @@
+/* auto_irq.c: Auto-configure IRQ lines for linux. */
+/*
+    Written 1994 by Donald Becker.
+
+    The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+    Center of Excellence in Space Data and Information Sciences
+      Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+    This code is a general-purpose IRQ line detector for devices with
+    jumpered IRQ lines.  If you can make the device raise an IRQ (and
+    that IRQ line isn't already being used), these routines will tell
+    you what IRQ line it's using -- perfect for those oh-so-cool boot-time
+    device probes!
+
+    To use this, first call autoirq_setup(timeout). TIMEOUT is how many
+    'jiffies' (1/100 sec.) to detect other devices that have active IRQ lines,
+    and can usually be zero at boot.  'autoirq_setup()' returns the bit
+    vector of nominally-available IRQ lines (lines may be physically in-use,
+    but not yet registered to a device).
+    Next, set up your device to trigger an interrupt.
+    Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
+    most recently active.  The TIMEOUT should usually be zero, but may
+    be set to the number of jiffies to wait for a slow device to raise an IRQ.
+
+    The idea of using the setup timeout to filter out bogus IRQs came from
+    the serial driver.
+*/
+
+
+#ifdef version
+static char *version=
+"auto_irq.c:v1.11 Donald Becker (becker@cesdis.gsfc.nasa.gov)";
+#endif
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/netdevice.h>
+
+struct device *irq2dev_map[32] = {0, 0, /* ... zeroed */};
+
+unsigned long irqs_busy = 0x2147;		/* The set of fixed IRQs (keyboard, timer, etc) */
+unsigned long irqs_used = 0x0001;		/* The set of fixed IRQs sometimes enabled. */
+unsigned long irqs_reserved = 0x0000;		/* An advisory "reserved" table. */
+unsigned long irqs_shared = 0x0000;		/* IRQ lines "shared" among conforming cards.*/
+
+static volatile unsigned long irq_bitmap;	/* The irqs we actually found. */
+static unsigned long irq_handled;		/* The irq lines we have a handler on. */
+static volatile int irq_number;			/* The latest irq number we actually found. */
+
+static void autoirq_probe(int irq, struct pt_regs * regs)
+{
+	irq_number = irq;
+	set_bit(irq, (void *)&irq_bitmap);	/* irq_bitmap |= 1 << irq; */
+	disable_irq(irq);
+	return;
+}
+
+int autoirq_setup(int waittime)
+{
+	int i, mask;
+	int timeout = jiffies + waittime;
+	int boguscount = (waittime*loops_per_sec) / 100;
+
+	irq_handled = 0;
+	for (i = 0; i < 16; i++) {
+		if (test_bit(i, &irqs_busy) == 0
+			&& request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe") == 0)
+			set_bit(i, (void *)&irq_handled);	/* irq_handled |= 1 << i;*/
+	}
+	/* Update our USED lists. */
+	irqs_used |= ~irq_handled;
+	irq_number = 0;
+	irq_bitmap = 0;
+
+	/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
+	while (timeout > jiffies  &&  --boguscount > 0)
+		;
+
+	for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
+		if (irq_bitmap & irq_handled & mask) {
+			irq_handled &= ~mask;
+#ifdef notdef
+			printk(" Spurious interrupt on IRQ %d\n", i);
+#endif
+			free_irq(i);
+		}
+	}
+	return irq_handled;
+}
+
+int autoirq_report(int waittime)
+{
+	int i;
+	int timeout = jiffies+waittime;
+	int boguscount = (waittime*loops_per_sec) / 100;
+
+	/* Hang out at least <waittime> jiffies waiting for the IRQ. */
+
+	while (timeout > jiffies  &&  --boguscount > 0)
+		if (irq_number)
+			break;
+
+	/* Retract the irq handlers that we installed. */
+	for (i = 0; i < 16; i++) {
+		if (test_bit(i, (void *)&irq_handled))
+			free_irq(i);
+	}
+	return irq_number;
+}
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urNwbB linux/arch/arm/drivers/net/ether1.c linux.arm/arch/arm/drivers/net/ether1.c
--- linux/arch/arm/drivers/net/ether1.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ether1.c	Sun Feb 18 09:28:09 1996
@@ -0,0 +1,1049 @@
+/*
+ * linux/arch/arm/drivers/net/ether1.c
+ *
+ * Acorn ether1 driver (82586 chip)
+ *
+ * (c) 1996 Russell King
+ */
+
+/*
+ * We basically keep two queues in the cards memory - one for transmit
+ * and one for receive.  Each has a head and a tail.  The head is where
+ * we/the chip adds packets to be transmitted/received, and the tail
+ * is where the transmitter has got to/where the receiver will stop.
+ * Both of these queues are circular, and since the chip is running
+ * all the time, we have to be careful when we modify the pointers etc
+ * so that the buffer memory is valid all the time.
+ */
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#define CLAIM_IRQ_AT_OPEN
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/ecard.h>
+
+#define __ETHER1_C
+#include "ether1.h"
+
+static unsigned int net_debug = NET_DEBUG;
+
+#define FUNC_PROLOGUE \
+	struct ether1_priv *priv = (struct ether1_priv *)dev->priv
+
+#define BUFFER_SIZE	65536
+#define TX_AREA_START	0x0100
+#define TX_AREA_END	0x5000
+#define RX_AREA_START	0x5000
+#define RX_AREA_END	0xfc00
+
+#define tx_done(dev) 0
+/* ------------------------------------------------------------------------- */
+static char *version = "ether1 ethernet driver (c) 1995 Russell King V1.00\n";
+
+#define BUS_16 16
+#define BUS_8  8
+
+static int ether1_prods[] = { 0x0003 };
+static int ether1_manus[] = { 0x0000 };
+
+/* ------------------------------------------------------------------------- */
+#define ether1_inw(dev, addr, type, offset) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset))
+#define ether1_outw(dev, val, addr, type, offset) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset))
+
+static inline unsigned short
+ether1_inw_p (struct device *dev, int addr)
+{
+    outb (addr >> 12, REG_PAGE);
+
+    return inw (ETHER1_RAM + ((addr & 4095) >> 1));
+}
+
+static inline void
+ether1_outw_p (struct device *dev, unsigned short val, int addr)
+{
+    outb (addr >> 12, REG_PAGE);
+    outw (val, ETHER1_RAM + ((addr & 4095) >> 1));
+}
+
+static inline void *
+ether1_inswb (int addr, void *data, unsigned int len)
+{
+    int used;
+
+    __asm__ __volatile__(
+    "	subs	%3, %3, #2\n"
+    "	bmi	2f\n"
+    "1:	ldr	%0, [%1], #4\n"
+    "	strb	%0, [%2], #1\n"
+    "	mov	%0, %0, lsr #8\n"
+    "	strb	%0, [%2], #1\n"
+    "	subs	%3, %3, #2\n"
+    "	bpl	1b\n"
+    "2:"
+    : "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
+    : "1" (addr << 2), "2" (data), "3" (len));
+
+    return data;
+}
+
+static inline void *
+ether1_outswb (int addr, void *data, unsigned int len)
+{
+    int used;
+
+    __asm__ __volatile__(
+    "	subs	%3, %3, #2\n"
+    "	bmi	2f\n"
+    "1:	ldr	%0, [%2], #2\n"
+    "	mov	%0, %0, lsl #16\n"
+    "	orr	%0, %0, %0, lsr #16\n"
+    "	str	%0, [%1], #4\n"
+    "	subs	%3, %3, #2\n"
+    "	bpl	1b\n"
+    "2:"
+    : "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
+    : "1" (addr << 2), "2" (data), "3" (len));
+
+    return data;
+}
+
+
+static void
+ether1_writebuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
+{
+    unsigned int page, thislen, offset;
+
+    offset = start & 4095;
+
+    for (page = start >> 12; length; page++) {
+	outb (page, REG_PAGE);
+	if (offset + length > 4096) {
+	    length -= 4096 - offset;
+	    thislen = 4096 - offset;
+	} else {
+	    thislen = length;
+	    length = 0;
+	}
+
+	data = ether1_outswb (ETHER1_RAM + (offset >> 1), data, thislen);
+	offset = 0;
+    }
+}
+
+static void
+ether1_readbuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
+{
+    unsigned int page, thislen, offset;
+
+    offset = start & 4095;
+
+    for (page = start >> 12; length; page++) {
+	outb (page, REG_PAGE);
+	if (offset + length > 4096) {
+	    length -= 4096 - offset;
+	    thislen = 4096 - offset;
+	} else {
+	    thislen = length;
+	    length = 0;
+	}
+
+	data = ether1_inswb (ETHER1_RAM + (offset >> 1), data, thislen);
+	offset = 0;
+    }
+}
+
+static int
+ether1_ramtest (struct device *dev, unsigned char byte)
+{
+    unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL);
+    int i, ret = BUFFER_SIZE;
+    int max_errors = 4;
+    int bad = -1;
+
+    if (!buffer)
+	return 1;
+
+    memset (buffer, byte, BUFFER_SIZE);
+    ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE);
+    memset (buffer, byte ^ 0xff, BUFFER_SIZE);
+    ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE);
+
+    for (i = 0; i < BUFFER_SIZE; i++) {
+	if (buffer[i] != byte) {
+	    if (max_errors >= 0 && bad != buffer[i]) {
+		if (bad != -1)
+		    printk ("\n");
+		printk ("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
+			dev->name, buffer[i], byte, i);
+		ret = -ENODEV;
+		max_errors --;
+		bad = buffer[i];
+	    }
+	} else {
+	    if (bad != -1) {
+		printk (" - 0x%04X\n", i);
+		bad = -1;
+	    }
+	}
+    }
+
+    if (bad != -1)
+	printk (" - 0x%04X\n", BUFFER_SIZE);
+    kfree (buffer);
+
+    return ret;
+}
+
+static int
+ether1_reset (struct device *dev)
+{
+    outb (CTRL_RST|CTRL_ACK, REG_CONTROL);
+    return BUS_16;
+}
+
+static int
+ether1_init_2 (struct device *dev)
+{
+    int i;
+    dev->mem_start = 0;
+    
+    i = ether1_ramtest (dev, 0x5a);
+
+    if (i > 0)
+	i = ether1_ramtest (dev, 0x1e);
+
+    if (i <= 0)
+    	return -ENODEV;
+
+    dev->mem_end = i;
+    
+    return 0;
+}
+
+/*
+ * These are the structures that are loaded into the ether RAM card to
+ * initialise the 82586
+ */
+
+/* at 0x0100 */
+#define NOP_ADDR	(TX_AREA_START)
+#define NOP_SIZE	(0x06)
+static nop_t  init_nop  = {
+    0,
+    CMD_NOP,
+    NOP_ADDR
+};
+
+/* at 0x003a */
+#define TDR_ADDR	(0x003a)
+#define TDR_SIZE	(0x08)
+static tdr_t  init_tdr	= {
+    0,
+    CMD_TDR | CMD_INTR,
+    NOP_ADDR,
+    0
+};
+
+/* at 0x002e */
+#define MC_ADDR		(0x002e)
+#define MC_SIZE		(0x0c)
+static mc_t   init_mc   = {
+    0,
+    CMD_SETMULTICAST,
+    TDR_ADDR,
+    0,
+    { { 0, } }
+};
+
+/* at 0x0022 */
+#define SA_ADDR		(0x0022)
+#define SA_SIZE		(0x0c)
+static sa_t   init_sa   = {
+    0,
+    CMD_SETADDRESS,
+    MC_ADDR,
+    { 0, }
+};
+
+/* at 0x0010 */
+#define CFG_ADDR	(0x0010)
+#define CFG_SIZE	(0x12)
+static cfg_t  init_cfg  = {
+    0,
+    CMD_CONFIG,
+    SA_ADDR,
+    4,
+    8,
+    CFG8_SRDY,
+    CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6),
+    0,
+};
+
+/* at 0x0000 */
+#define SCB_ADDR	(0x0000)
+#define SCB_SIZE	(0x10)
+static scb_t  init_scb  = {
+    0,
+    0,
+    CFG_ADDR,
+    RX_AREA_START,
+    0,
+    0,
+    0,
+    0
+};
+
+/* at 0xffee */
+#define ISCP_ADDR	(0xffee)
+#define ISCP_SIZE	(0x08)
+static iscp_t init_iscp = {
+    1,
+    SCB_ADDR,
+    0x0000,
+    0x0000
+};
+
+/* at 0xfff6 */
+#define SCP_ADDR	(0xfff6)
+#define SCP_SIZE	(0x0a)
+static scp_t  init_scp  = {
+    SCP_SY_16BBUS,
+    { 0, 0 },
+    ISCP_ADDR,
+    0
+};
+
+#define RFD_SIZE	(0x16)
+static rfd_t  init_rfd	= {
+    0,
+    0,
+    0,
+    0,
+    { 0, },
+    { 0, },
+    0
+};
+
+#define RBD_SIZE	(0x0a)
+static rbd_t  init_rbd	= {
+    0,
+    0,
+    0,
+    0,
+    ETH_FRAME_LEN + 8
+};
+
+#define TX_SIZE		(0x08)
+#define TBD_SIZE	(0x08)
+
+static int
+ether1_init_for_open (struct device *dev)
+{
+    FUNC_PROLOGUE;
+    int i, status, addr, next, next2;
+
+    for (i = 0; i < 6; i++)
+	init_sa.sa_addr[i] = dev->dev_addr[i];
+
+    /* load data structures into ether1 RAM */
+    ether1_writebuffer (dev, &init_scp,  SCP_ADDR,  SCP_SIZE);
+    ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE);
+    ether1_writebuffer (dev, &init_scb,  SCB_ADDR,  SCB_SIZE);
+    ether1_writebuffer (dev, &init_cfg,  CFG_ADDR,  CFG_SIZE);
+    ether1_writebuffer (dev, &init_sa,   SA_ADDR,   SA_SIZE);
+    ether1_writebuffer (dev, &init_mc,   MC_ADDR,   MC_SIZE);
+    ether1_writebuffer (dev, &init_tdr,  TDR_ADDR,  TDR_SIZE);
+    ether1_writebuffer (dev, &init_nop,  NOP_ADDR,  NOP_SIZE);
+
+    /*
+     * setup circularly linked list of { rfd, rbd, buffer }, with
+     * all rfds circularly linked, rbds circularly linked.
+     * First rfd is linked to scp, first rbd is linked to first
+     * rfd.  Last rbd has a suspend command.
+     */
+    addr = RX_AREA_START;
+    do {
+	next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+	next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+
+	if (next2 >= RX_AREA_END) {
+	    next = RX_AREA_START;
+	    init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND;
+	    priv->rx_tail = addr;
+	} else
+	    init_rfd.rfd_command = 0;
+	if (addr == RX_AREA_START)
+	    init_rfd.rfd_rbdoffset = addr + RFD_SIZE;
+	else
+	    init_rfd.rfd_rbdoffset = 0;
+	init_rfd.rfd_link = next;
+	init_rbd.rbd_link = next + RFD_SIZE;
+	init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE;
+
+	ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE);
+	ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE);
+	addr = next;
+    } while (next2 < RX_AREA_END);
+
+    priv->tx_link = NOP_ADDR;
+    priv->tx_head = NOP_ADDR + NOP_SIZE;
+    priv->tx_tail = TDR_ADDR;
+    priv->rx_head = RX_AREA_START;
+
+    /* release reset & give 586 a prod */
+    priv->resetting = 1;
+    outb (CTRL_RST, REG_CONTROL);
+    outb (0, REG_CONTROL);
+    outb (CTRL_CA, REG_CONTROL);
+
+    /* 586 should now unset iscp.busy */
+    i = jiffies + HZ/2;
+    while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy) == 1) {
+	if (jiffies > i) {
+	    printk ("%s: can't initialise 82586: iscp is busy\n", dev->name);
+	    return 1;
+	}
+    }
+
+    /* check status of commands that we issued */
+    i += HZ/10;
+    while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status)) & STAT_COMPLETE) == 0) {
+	if (jiffies > i)
+	    break;
+    }
+
+    if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+	printk ("%s: can't initialise 82586: config status %04X\n", dev->name, status);
+	ether1_reset (dev);
+	return 1;
+    }
+
+    i += HZ/10;
+    while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status)) & STAT_COMPLETE) == 0) {
+	if (jiffies > i)
+	    break;
+    }
+
+    if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+	printk ("%s: can't initialise 82586: set address status %04X\n", dev->name, status);
+	ether1_reset (dev);
+	return 1;
+    }
+
+    i += HZ/10;
+    while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status)) & STAT_COMPLETE) == 0) {
+	if (jiffies > i)
+	    break;
+    }
+
+    if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+	printk ("%s: can't initialise 82586: set multicast status %04X\n", dev->name, status);
+	ether1_reset (dev);
+	return 1;
+    }
+
+    i += HZ;
+    while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status)) & STAT_COMPLETE) == 0) {
+	if (jiffies > i)
+	    break;
+    }
+
+    if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
+	printk ("%s: can't tdr (ignored)\n", dev->name);
+	return 0;
+    }
+
+    status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result);
+    if (status & TDR_XCVRPROB)
+	printk ("%s: i/f failed tdr: transceiver problem\n", dev->name);
+    else
+    if (status & (TDR_SHORT|TDR_OPEN)) {
+#ifdef FANCY
+	printk ("%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name,
+		status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10,
+		(status & TDR_TIME) % 10);
+#else
+	printk ("%s: i/f failed tdr: cable %s %d clks away\n", dev->name,
+		status & TDR_SHORT ? "short" : "open", (status & TDR_TIME));
+#endif
+    }
+
+    return 0;
+}
+
+static int
+ether1_probe1 (struct device *dev)
+{
+    static unsigned int version_printed = 0;
+    struct ether1_priv *priv;
+    int i;
+
+    if (!dev->priv)
+	dev->priv = kmalloc (sizeof (struct ether1_priv), GFP_KERNEL);
+
+    if (!dev->priv)
+    	return 1;
+
+    priv = (struct ether1_priv *)dev->priv;
+    memset (priv, 0, sizeof (struct ether1_priv));
+
+    if ((priv->bus_type = ether1_reset (dev)) == 0) {
+	kfree (dev->priv);
+	return 1;
+    }
+
+    if (net_debug && version_printed++ == 0)
+	printk (version);
+
+    printk ("%s: ether1 found (bus width %d), ", dev->name, priv->bus_type);
+
+    for (i = 0; i < 6; i++)
+	printk (i==0?" %2.2x":i==5?":%2.2x\n":":%2.2x", dev->dev_addr[i]);
+
+    if (ether1_init_2 (dev)) {
+	kfree (dev->priv);
+	return 1;
+    }
+
+    dev->open		    = ether1_open;
+    dev->stop		    = ether1_close;
+    dev->hard_start_xmit    = ether1_sendpacket;
+    dev->get_stats	    = ether1_getstats;
+    dev->set_multicast_list = ether1_setmulticastlist;
+
+    /* Fill in the fields of the device structure with ethernet values */
+    ether_setup (dev);
+
+#ifndef CLAIM_IRQ_AT_OPEN
+    if (request_irq (dev->irq, ether1_interrupt, 0, "ether1")) {
+	kfree (dev->priv);
+	return -EAGAIN;
+    }
+
+    irq2dev_map[dev->irq] = dev;
+#endif
+    return 0;
+}	
+    
+/* ------------------------------------------------------------------------- */
+
+static void
+ether1_addr (struct device *dev)
+{
+    int i;
+    
+    for (i = 0; i < 6; i++)
+	dev->dev_addr[i] = inb (IDPROM_ADDRESS + i);
+}
+
+int
+ether1_probe (struct device *dev)
+{
+#ifndef MODULE
+    struct expansion_card *ec;
+
+    if (!dev)
+	return ENODEV;
+
+    if ((ec = ecard_find (0, sizeof (ether1_prods) / sizeof (int), ether1_prods, ether1_manus)) == NULL)
+	return ENODEV;
+
+    dev->base_addr = (((unsigned long)ec->r_podaddr & ~0x3c0000) + 0x340000) >> 2;
+    dev->irq       = ec->irq;
+
+    ecard_claim (ec);
+
+#endif
+    ether1_addr (dev);
+
+    if (ether1_probe1 (dev) == 0)
+	return 0;
+    return ENODEV;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int
+ether1_txalloc (struct device *dev, int size)
+{
+    FUNC_PROLOGUE;
+    int start, tail;
+
+    size = (size + 1) & ~1;
+    tail = priv->tx_tail;
+
+    if (priv->tx_head + size > TX_AREA_END) {
+	if (tail > priv->tx_head)
+	    return -1;
+	start = TX_AREA_START;
+	if (start + size > tail)
+	    return -1;
+	priv->tx_head = start + size;
+    } else {
+	if (priv->tx_head < tail && (priv->tx_head + size) < tail)
+	    return -1;
+	start = priv->tx_head;
+	priv->tx_head += size;
+    }
+
+    return start;
+}
+
+static int
+ether1_open (struct device *dev)
+{
+    FUNC_PROLOGUE;
+#ifdef CLAIM_IRQ_AT_OPEN
+    if (request_irq (dev->irq, ether1_interrupt, 0, "ether1")) {
+	kfree (dev->priv);
+	return -EAGAIN;
+    }
+
+    irq2dev_map[dev->irq] = dev;
+#endif
+    MOD_INC_USE_COUNT;
+
+    memset (&priv->stats, 0, sizeof (struct enet_statistics));
+
+    ether1_init_for_open (dev);
+
+    dev->tbusy = 0;
+    dev->interrupt = 0;
+    dev->start = 1;
+
+    return 0;
+}
+
+static int
+ether1_sendpacket (struct sk_buff *skb, struct device *dev)
+{
+    FUNC_PROLOGUE;
+
+    if (dev->tbusy) {
+	/*
+	 * If we get here, some higher level has decided that we are broken.
+	 * There should really be a "kick me" function call instead.
+	 */
+	int tickssofar = jiffies - dev->trans_start;
+
+	if (tickssofar < 5)
+	    return 1;
+
+	printk ("%s: transmit timeout, network cable problem?\n", dev->name);
+
+	/* Try to restart the adapter. */
+	dev->tbusy = 0;
+	dev->trans_start = jiffies;
+    }
+
+    /*
+     * If some higher layer thinks we've missed a tx-done interrupt
+     * we are passed NULL.  Caution: dev_tint() handles the cli()/sti()
+     * itself.
+     */
+    if (skb == NULL) {
+	dev_tint (dev);
+	return 0;
+    }
+
+    /*
+     * Block a timer-based transmit from overlapping.  This could better be
+     * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+     */
+    if (set_bit (0, (void *)&dev->tbusy) != 0)
+	printk ("%s: transmitter access conflict.\n", dev->name);
+    else {
+	int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+	int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr;
+	tx_t tx;
+	tbd_t tbd;
+	nop_t nop;
+	/*
+	 * insert packet followed by a nop
+	 */
+	txaddr = ether1_txalloc (dev, TX_SIZE);
+	tbdaddr = ether1_txalloc (dev, TBD_SIZE);
+	dataddr = ether1_txalloc (dev, len);
+	nopaddr = ether1_txalloc (dev, NOP_SIZE);
+
+	tx.tx_status = 0;
+	tx.tx_command = CMD_TX | CMD_INTR;
+	tx.tx_link = nopaddr;
+	tx.tx_tbdoffset = tbdaddr;
+	tbd.tbd_opts = TBD_EOL | len;
+	tbd.tbd_link = I82586_NULL;
+	tbd.tbd_bufl = dataddr;
+	tbd.tbd_bufh = 0;
+	nop.nop_status = 0;
+	nop.nop_command = CMD_NOP;
+	nop.nop_link = nopaddr;
+
+	ether1_writebuffer (dev, &tx, txaddr, TX_SIZE);
+	ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE);
+	ether1_writebuffer (dev, skb->data, dataddr, len);
+	ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE);
+	tmp = priv->tx_link;
+	priv->tx_link = nopaddr;
+
+	/* now reset the previous nop pointer */
+	ether1_outw (dev, txaddr, tmp, nop_t, nop_link);
+
+	/* handle transmit */
+	dev->trans_start = jiffies;
+
+	/* check to see if we have room for a full sized ether frame */
+	tmp = priv->tx_head;
+	tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+	priv->tx_head = tmp;
+	if (tst != -1)
+	    dev->tbusy = 0;
+    }
+    dev_kfree_skb (skb, FREE_WRITE);
+
+    return 0;
+}
+
+static void
+ether1_xmit_done (struct device *dev)
+{
+    FUNC_PROLOGUE;
+    nop_t nop;
+    int caddr, tst;
+
+    caddr = priv->tx_tail;
+
+again:
+    ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+
+    switch (nop.nop_command & CMD_MASK) {
+    case CMD_TDR:
+	/* special case */
+	if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset) != (unsigned short)I82586_NULL) {
+	    ether1_outw (dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, scb_command);
+	    outb (CTRL_CA, REG_CONTROL);
+	}
+	priv->tx_tail = NOP_ADDR;
+	return;
+
+    case CMD_NOP:
+	if (nop.nop_link == caddr) {
+	    printk ("%s: strange command complete with no tx command!\n", dev->name);
+	    return;
+	}
+	caddr = nop.nop_link;
+	goto again;
+
+    case CMD_TX:
+	if (nop.nop_status & STAT_COMPLETE)
+	    break;
+	printk ("%s: strange command complete without completed command!\n", dev->name);
+	return;
+
+    default:
+	printk ("%s: strange command %d complete!\n", dev->name, nop.nop_command & CMD_MASK);
+	return;
+    }
+
+    while (nop.nop_status & STAT_COMPLETE) {
+	if (nop.nop_status & STAT_OK) {
+	    priv->stats.tx_packets ++;
+	    priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS);
+	} else {
+	    priv->stats.tx_errors ++;
+
+	    if (nop.nop_status & STAT_COLLAFTERTX) {
+		priv->stats.collisions ++;
+		printk ("%s: late collision detected\n", dev->name);
+	    }
+	    if (nop.nop_status & STAT_NOCARRIER) {
+		priv->stats.tx_carrier_errors ++;
+		printk ("%s: no carrier\n", dev->name);
+	    }
+	    if (nop.nop_status & STAT_TXLOSTCTS)
+		printk ("%s: cts lost\n", dev->name);
+	    if (nop.nop_status & STAT_TXSLOWDMA) {
+		priv->stats.tx_fifo_errors ++;
+		printk ("%s: fifo error - dma underrun\n", dev->name);
+	    }
+	    if (nop.nop_status & STAT_COLLEXCESSIVE) {
+		priv->stats.collisions += 16;
+		printk ("%s: excessive collisions\n", dev->name);
+	    }
+	}
+
+	if (nop.nop_link == caddr) {
+	    printk ("%s: tx buffer chaining error: tx command points to itself\n", dev->name);
+	    break;
+	}
+
+	caddr = nop.nop_link;
+	ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+	if ((nop.nop_command & CMD_MASK) != CMD_NOP) {
+	    printk ("%s: tx buffer chaining error: no nop after tx command\n", dev->name);
+	    break;
+	}
+
+	if (caddr == nop.nop_link)
+	    break;
+
+	caddr = nop.nop_link;
+	ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+	if ((nop.nop_command & CMD_MASK) != CMD_TX) {
+	    printk ("%s: tx buffer chaining error: no tx command after nop\n", dev->name);
+	    break;
+	}
+    }
+    priv->tx_tail = caddr;
+
+    caddr = priv->tx_head;
+    tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+    priv->tx_head = caddr;
+    if (tst != -1)
+	dev->tbusy = 0;
+    
+    mark_bh (NET_BH);
+}
+
+static void
+ether1_recv_done (struct device *dev)
+{
+    FUNC_PROLOGUE;
+    int status;
+    int nexttail, rbdaddr;
+    rbd_t rbd;
+
+    do {
+	status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status);
+	if ((status & RFD_COMPLETE) == 0)
+	    break;
+
+	rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset);
+	ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE);
+
+	if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) {
+	    int length = rbd.rbd_status & RBD_ACNT;
+	    struct sk_buff *skb;
+
+	    length = (length + 1) & ~1;
+	    skb = dev_alloc_skb (length + 2);
+
+	    if (skb) {
+		skb->dev = dev;
+		skb_reserve (skb, 2);
+
+		ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
+
+		skb->protocol = eth_type_trans (skb, dev);
+		netif_rx (skb);
+		priv->stats.rx_packets ++;
+	    } else
+		priv->stats.rx_dropped ++;
+	} else {
+	    printk ("%s: %s\n", dev->name, (rbd.rbd_status & RBD_EOF) ? "oversized packet" :
+	    		"acnt not valid");
+	    priv->stats.rx_dropped ++;
+	}
+
+	nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link);
+	/* nexttail should be rx_head */
+	if (nexttail != priv->rx_head)
+	    printk ("%s: receiver buffer chaining error (%04X != %04X)\n", dev->name, nexttail,
+			priv->rx_head);
+	ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command);
+	ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command);
+	ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status);
+	ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset);
+	
+	priv->rx_tail = nexttail;
+	priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link);
+    } while (1);
+}
+
+static void
+ether1_interrupt (int irq, struct pt_regs *regs)
+{
+    struct device *dev = irq2dev_map[irq];
+    FUNC_PROLOGUE;
+    int status;
+
+    if (dev == NULL) {
+	printk ("ether1: interrupt %d from unknown device\n", irq);
+	return;
+    }
+
+    dev->interrupt = 1;
+
+    status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status);
+    if (status ) {
+	ether1_outw (dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX),
+			SCB_ADDR, scb_t, scb_command);
+	outb (CTRL_CA | CTRL_ACK, REG_CONTROL);
+	if (status & SCB_STCX) {
+	    ether1_xmit_done (dev);
+	}
+	if (status & SCB_STCNA) {
+	    if (priv->resetting == 0)
+		printk ("%s: CU went not ready ???\n", dev->name);
+	    if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset) != (unsigned short)I82586_NULL) {
+		ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command);
+		outb (CTRL_CA, REG_CONTROL);
+	    }
+	    if (priv->resetting == 2)
+		priv->resetting = 0;
+	}
+	if (status & SCB_STFR) {
+	    ether1_recv_done (dev);
+	}
+	if (status & SCB_STRNR) {
+	    printk ("%s: RU went not ready\n", dev->name);
+	    printk ("RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset));
+	}
+    } else
+        outb (CTRL_ACK, REG_CONTROL);
+
+    dev->interrupt = 0;
+}
+
+static int
+ether1_close (struct device *dev)
+{
+#ifdef CLAIM_IRQ_AT_OPEN
+    free_irq (dev->irq);
+    irq2dev_map[dev->irq] = NULL;
+#endif
+
+    ether1_reset (dev);
+
+    dev->start = 0;
+    dev->tbusy = 0;
+
+    MOD_DEC_USE_COUNT;
+
+    return 0;
+}
+
+static struct enet_statistics *
+ether1_getstats (struct device *dev)
+{
+    FUNC_PROLOGUE;
+    return &priv->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets.
+ * num_addrs == 0	Normal mode, clear multicast list.
+ * num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+ *			best-effort filtering.
+ */
+static void
+ether1_setmulticastlist (struct device *dev, int num_addrs, void *addrs)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static char ethernames[4][9] = {
+    "        ",
+    "        ",
+    "        ",
+    "        "
+};
+static struct device *my_ethers[4];
+static struct expansion_card *ec[4];
+
+int
+init_module (void)
+{
+    int i;
+
+    for (i = 0; i < 4; i++) {
+	my_ethers[i] = NULL;
+	ec[i] = NULL;
+    }
+
+    i = 0;
+
+    do {
+	if ((ec[i] = ecard_find(0, sizeof(ether1_prods) / sizeof(int), ether1_prods, ether1_manus))
+		== NULL)
+	    break;
+
+	my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL);
+	memset (my_ethers[i], 0, sizeof (struct device));
+
+	my_ethers[i]->irq = ec[i]->irq;
+	my_ethers[i]->base_addr = (((unsigned long)ec[i]->r_podaddr & ~0x3c0000) + 0x340000) >> 2;
+	my_ethers[i]->init = ether1_probe;
+	my_ethers[i]->name = ethernames[i];
+
+	ecard_claim (ec[i]);
+
+	if (register_netdev (my_ethers[i]) != 0) {
+	    for (i = 0; i < 4; i++) {
+		if (my_ethers[i]) {
+		    kfree (my_ethers[i]);
+		    my_ethers[i] = NULL;
+		}
+		if (ec[i]) {
+		    ecard_release (ec[i]);
+		    ec[i] = NULL;
+		}
+	    }
+	    return -EIO;
+	}
+	i++;
+    } while (i < 4);
+
+    return i != 0 ? 0 : -ENODEV;
+}
+
+void
+cleanup_module (void)
+{
+    if (MOD_IN_USE) {
+	printk ("%s: device busy, remove delayed\n", "ether1");
+    } else {
+	int i;
+
+	for (i = 0; i < 4; i++) {
+	    if (my_ethers[i]) {
+		unregister_netdev (my_ethers[i]);
+		my_ethers[i] = NULL;
+	    }
+	    if (ec[i]) {
+	    	ecard_release (ec[i]);
+	    	ec[i] = NULL;
+	    }
+	}
+    }
+}
+#endif /* MODULE */
diff -urNwbB linux/arch/arm/drivers/net/ether1.h linux.arm/arch/arm/drivers/net/ether1.h
--- linux/arch/arm/drivers/net/ether1.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ether1.h	Sat Feb 17 10:05:58 1996
@@ -0,0 +1,280 @@
+/*
+ * linux/arch/arm/drivers/net/ether1.h
+ *
+ * network driver for Acorn Ether1 cards.
+ *
+ * (c) 1996 Russell King
+ */
+
+#ifndef _LINUX_ether1_H
+#define _LINUX_ether1_H
+
+#ifdef __ETHER1_C
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+
+/* Page register */
+#define REG_PAGE	(dev->base_addr + 0x00)
+
+/* Control register */
+#define REG_CONTROL	(dev->base_addr + 0x01)
+#define CTRL_RST	0x01
+#define CTRL_LOOPBACK	0x02
+#define CTRL_CA		0x04
+#define CTRL_ACK	0x08
+
+#define ETHER1_RAM	(dev->base_addr + 0x800)
+
+/* HW address */
+#define IDPROM_ADDRESS	(dev->base_addr + 0x09)
+
+struct ether1_priv {
+	struct enet_statistics stats;
+	int tx_link;
+	int tx_head;
+	volatile int tx_tail;
+	volatile int rx_head;
+	volatile int rx_tail;
+	char bus_type;
+	char resetting;
+};
+
+static int ether1_open (struct device *dev);
+static int ether1_sendpacket (struct sk_buff *skb, struct device *dev);
+static void ether1_interrupt (int irq, struct pt_regs *regs);
+static int ether1_close (struct device *dev);
+static struct enet_statistics *ether1_getstats (struct device *dev);
+static void ether1_setmulticastlist (struct device *dev, int num_addrs, void *addrs);
+
+#define I82586_NULL (-1)
+
+typedef struct { /* tdr */
+	unsigned short tdr_status;
+	unsigned short tdr_command;
+	unsigned short tdr_link;
+	unsigned short tdr_result;
+#define TDR_TIME	(0x7ff)
+#define TDR_SHORT	(1 << 12)
+#define TDR_OPEN	(1 << 13)
+#define TDR_XCVRPROB	(1 << 14)
+#define TDR_LNKOK	(1 << 15)
+} tdr_t;
+
+typedef struct { /* transmit */
+	unsigned short tx_status;
+	unsigned short tx_command;
+	unsigned short tx_link;
+	unsigned short tx_tbdoffset;
+} tx_t;
+
+typedef struct { /* tbd */
+	unsigned short tbd_opts;
+#define TBD_CNT		(0x3fff)
+#define TBD_EOL		(1 << 15)
+	unsigned short tbd_link;
+	unsigned short tbd_bufl;
+	unsigned short tbd_bufh;
+} tbd_t;
+
+typedef struct { /* rfd */
+	unsigned short rfd_status;
+#define RFD_NOEOF	(1 << 6)
+#define RFD_FRAMESHORT	(1 << 7)
+#define RFD_DMAOVRN	(1 << 8)
+#define RFD_NORESOURCES	(1 << 9)
+#define RFD_ALIGNERROR	(1 << 10)
+#define RFD_CRCERROR	(1 << 11)
+#define RFD_OK		(1 << 13)
+#define RFD_FDCONSUMED	(1 << 14)
+#define RFD_COMPLETE	(1 << 15)
+	unsigned short rfd_command;
+#define RFD_CMDSUSPEND	(1 << 14)
+#define RFD_CMDEL	(1 << 15)
+	unsigned short rfd_link;
+	unsigned short rfd_rbdoffset;
+	unsigned char  rfd_dest[6];
+	unsigned char  rfd_src[6];
+	unsigned short rfd_len;
+} rfd_t;
+
+typedef struct { /* rbd */
+	unsigned short rbd_status;
+#define RBD_ACNT	(0x3fff)
+#define RBD_ACNTVALID	(1 << 14)
+#define RBD_EOF		(1 << 15)
+	unsigned short rbd_link;
+	unsigned short rbd_bufl;
+	unsigned short rbd_bufh;
+	unsigned short rbd_len;
+} rbd_t;
+
+typedef struct { /* nop */
+	unsigned short nop_status;
+	unsigned short nop_command;
+	unsigned short nop_link;
+} nop_t;
+
+typedef struct { /* set multicast */
+	unsigned short mc_status;
+	unsigned short mc_command;
+	unsigned short mc_link;
+	unsigned short mc_cnt;
+	unsigned char  mc_addrs[1][6];
+} mc_t;
+
+typedef struct { /* set address */
+	unsigned short sa_status;
+	unsigned short sa_command;
+	unsigned short sa_link;
+	unsigned char  sa_addr[6];
+} sa_t;
+
+typedef struct { /* config command */
+	unsigned short cfg_status;
+	unsigned short cfg_command;
+	unsigned short cfg_link;
+	unsigned char  cfg_bytecnt;	/* size foll data: 4 - 12		 */
+	unsigned char  cfg_fifolim;	/* FIFO threshold			 */
+	unsigned char  cfg_byte8;
+#define CFG8_SRDY	(1 << 6)
+#define CFG8_SAVEBADF	(1 << 7)
+	unsigned char  cfg_byte9;
+#define CFG9_ADDRLEN(x)	(x)
+#define CFG9_ADDRLENBUF	(1 << 3)
+#define CFG9_PREAMB2	(0 << 4)
+#define CFG9_PREAMB4	(1 << 4)
+#define CFG9_PREAMB8	(2 << 4)
+#define CFG9_PREAMB16	(3 << 4)
+#define CFG9_ILOOPBACK	(1 << 6)
+#define CFG9_ELOOPBACK	(1 << 7)
+	unsigned char  cfg_byte10;
+#define CFG10_LINPRI(x)	(x)
+#define CFG10_ACR(x)	(x << 4)
+#define CFG10_BOFMET	(1 << 7)
+	unsigned char  cfg_ifs;
+	unsigned char  cfg_slotl;
+	unsigned char  cfg_byte13;
+#define CFG13_SLOTH(x)	(x)
+#define CFG13_RETRY(x)	(x << 4)
+	unsigned char  cfg_byte14;
+#define CFG14_PROMISC	(1 << 0)
+#define CFG14_DISBRD	(1 << 1)
+#define CFG14_MANCH	(1 << 2)
+#define CFG14_TNCRS	(1 << 3)
+#define CFG14_NOCRC	(1 << 4)
+#define CFG14_CRC16	(1 << 5)
+#define CFG14_BTSTF	(1 << 6)
+#define CFG14_FLGPAD	(1 << 7)
+	unsigned char  cfg_byte15;
+#define CFG15_CSTF(x)	(x)
+#define CFG15_ICSS	(1 << 3)
+#define CFG15_CDTF(x)	(x << 4)
+#define CFG15_ICDS	(1 << 7)
+	unsigned short cfg_minfrmlen;
+} cfg_t;
+
+typedef struct { /* scb */
+	unsigned short scb_status;	/* status of 82586			 */
+#define SCB_STRXMASK		(7 << 4)
+#define SCB_STRXIDLE		(0 << 4)
+#define SCB_STRXSUSP		(1 << 4)
+#define SCB_STRXNRES		(2 << 4)
+#define SCB_STRXRDY		(4 << 4)
+#define SCB_STCUMASK		(7 << 8)
+#define SCB_STCUIDLE		(0 << 8)
+#define SCB_STCUSUSP		(1 << 8)
+#define SCB_STCUACTV		(2 << 8)
+#define SCB_STRNR		(1 << 12)
+#define SCB_STCNA		(1 << 13)
+#define SCB_STFR		(1 << 14)
+#define SCB_STCX		(1 << 15)
+	unsigned short scb_command;	/* Next command				 */
+#define SCB_CMDRXSTART		(1 << 4)
+#define SCB_CMDRXRESUME		(2 << 4)
+#define SCB_CMDRXSUSPEND	(3 << 4)
+#define SCB_CMDRXABORT		(4 << 4)
+#define SCB_CMDCUCSTART		(1 << 8)
+#define SCB_CMDCUCRESUME	(2 << 8)
+#define SCB_CMDCUCSUSPEND	(3 << 8)
+#define SCB_CMDCUCABORT		(4 << 8)
+#define SCB_CMDACKRNR		(1 << 12)
+#define SCB_CMDACKCNA		(1 << 13)
+#define SCB_CMDACKFR		(1 << 14)
+#define SCB_CMDACKCX		(1 << 15)
+	unsigned short scb_cbl_offset;	/* Offset of first command unit		 */
+	unsigned short scb_rfa_offset;	/* Offset of first receive frame area	 */
+	unsigned short scb_crc_errors;	/* Properly aligned frame with CRC error */
+	unsigned short scb_aln_errors;	/* Misaligned frames			 */
+	unsigned short scb_rsc_errors;	/* Frames lost due to no space		 */
+	unsigned short scb_ovn_errors;	/* Frames lost due to slow bus		 */
+} scb_t;
+
+typedef struct { /* iscp */
+	unsigned short iscp_busy;	/* set by CPU before CA			 */
+	unsigned short iscp_offset;	/* offset of SCB			 */
+	unsigned short iscp_basel;	/* base of SCB				 */
+	unsigned short iscp_baseh;
+} iscp_t;
+
+    /* this address must be 0xfff6 */
+typedef struct { /* scp */
+	unsigned short scp_sysbus;	/* bus size */
+#define SCP_SY_16BBUS	0x00
+#define SCP_SY_8BBUS	0x01
+	unsigned short scp_junk[2];	/* junk */
+	unsigned short scp_iscpl;	/* lower 16 bits of iscp */
+	unsigned short scp_iscph;	/* upper 16 bits of iscp */
+} scp_t;
+
+/* commands */
+#define CMD_NOP			0
+#define CMD_SETADDRESS		1
+#define CMD_CONFIG		2
+#define CMD_SETMULTICAST	3
+#define CMD_TX			4
+#define CMD_TDR			5
+#define CMD_DUMP		6
+#define CMD_DIAGNOSE		7
+
+#define CMD_MASK		7
+
+#define CMD_INTR		(1 << 13)
+#define CMD_SUSP		(1 << 14)
+#define CMD_EOL			(1 << 15)
+
+#define STAT_COLLISIONS		(15)
+#define STAT_COLLEXCESSIVE	(1 << 5)
+#define STAT_COLLAFTERTX	(1 << 6)
+#define STAT_TXDEFERRED		(1 << 7)
+#define STAT_TXSLOWDMA		(1 << 8)
+#define STAT_TXLOSTCTS		(1 << 9)
+#define STAT_NOCARRIER		(1 << 10)
+#define STAT_FAIL		(1 << 11)
+#define STAT_ABORTED		(1 << 12)
+#define STAT_OK			(1 << 13)
+#define STAT_BUSY		(1 << 14)
+#define STAT_COMPLETE		(1 << 15)
+#endif
+#endif
+
+/*
+ * Ether1 card definitions:
+ *
+ * FAST accesses:
+ *	+0	Page register
+ * 			16 pages
+ *	+4	Control
+ *			'1' = reset
+ *			'2' = loopback
+ *			'4' = CA
+ *			'8' = int ack
+ *
+ * RAM at address + 0x2000
+ * Pod. Prod id = 3
+ * Words after ID block [base + 8 words]
+ *	+0 pcb issue (0x0c and 0xf3 invalid)
+ *	+1 - +6 eth hw address
+ */
+
diff -urNwbB linux/arch/arm/drivers/net/ether3.c linux.arm/arch/arm/drivers/net/ether3.c
--- linux/arch/arm/drivers/net/ether3.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ether3.c	Sat Mar  2 18:11:39 1996
@@ -0,0 +1,914 @@
+/*
+ * linux/drivers/net/ether3.c
+ *
+ * SEEQ nq8005 ethernet driver
+ *
+ * By Russell King.
+ *
+ * Changelog:
+ *  29/02/96:	RMK	Won't pass packets that are from our ethernet address
+ *			up to the higher levels - they're silently ignored.
+ *			I/F can now be put into multicast mode.  Receiver
+ *			routine optimised.
+ *
+ *  30/02/96:	RMK	Now claims interrupt at open when part of the kernel
+ *			rather than when a module.
+ *
+ *  02/03/96:	RMK	Various code cleanups
+ *
+ * TODO:
+ *  When we detect a fatal error on the interface, we should restart it.
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define CLAIM_IRQ_AT_OPEN
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
+
+#include "ether3.h"
+
+static unsigned int net_debug = NET_DEBUG;
+static int ether3_prods[] = { 0x00A4 };
+static int ether3_manus[] = { 0x0011 };
+
+static char *version = "ether3 ethernet driver (c) 1995 R.M.King v1.10\n";
+
+extern int inswb(int reg, void *buffer, int len);
+extern int outswb(int reg, void *buffer, int len);
+
+#define FUNC_PROLOGUE \
+	struct dev_priv *priv = (struct dev_priv *)dev->priv
+
+#define BUS_16 16
+#define BUS_8  8
+
+/*
+ * I'm not sure what address we should default to if the internal one
+ * is corrupted...
+ */
+unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+/* --------------------------------------------------------------------------- */
+
+/*
+ * write data to the buffer memory
+ */
+static void
+ether3_writebuffer(struct device *dev, void *data, int start, int length)
+{
+    FUNC_PROLOGUE;
+    int timeout = 1000000;
+
+    if (start != -1) {
+	outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+	while ((inw (REG_STATUS) & STAT_FIFOEMPTY) == 0)
+	    if (!timeout--) {
+		printk ("settxlim_16 broken\n");
+		priv->broken = 1;
+		return;
+	    }
+	outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+	outw (start, REG_DMAADDR);
+    }
+    if(length != 0)
+	outswb (REG_BUFWIN, data, length);
+}
+
+/*
+ * read data from the buffer memory
+ */
+static void
+ether3_readbuffer(struct device *dev, void *data, int start, int length)
+{
+    FUNC_PROLOGUE;
+    int timeout = 1000000;
+
+    if (start != -1) {
+	outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+	while ((inw( REG_STATUS) & STAT_FIFOEMPTY) == 0)
+	    if (!timeout--) {
+		printk ("setrxlim_16 broken\n");
+		priv->broken = 1;
+		return;
+	    }
+	outw (start, REG_DMAADDR);
+	outw (priv->regs.command | CMD_FIFOREAD, REG_COMMAND);
+    }
+    if(length != 0)
+	inswb (REG_BUFWIN, data, length);
+}
+
+/*
+ * Increment the use count, and switch LED on if necessary
+ */
+static inline void
+ether3_ledincrementuse (struct device *dev)
+{
+    FUNC_PROLOGUE;
+
+    if (++ priv->use == 1) {
+	priv->regs.config2 &= ~CFG2_CTRLO;
+	outw (priv->regs.config2 , REG_CONFIG2);
+    }
+}
+
+/*
+ * Decrement the use count and switch LED off if necessary
+ */
+static inline void
+ether3_leddecrementuse (struct device *dev)
+{
+    FUNC_PROLOGUE;
+
+    if (priv->use && -- priv->use == 0) {
+	priv->regs.config2 |= CFG2_CTRLO;
+	outw (priv->regs.config2, REG_CONFIG2);
+    }
+}
+
+/*
+ * Read the ethernet address string from the on board rom.
+ * This is an ascii string!!!
+ */
+static void
+ether3_addr(char *addr, struct expansion_card *ec)
+{
+    struct chunk_dir cd;
+    char *s;
+	
+    if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
+	int i;
+	for (i = 0; i<6; i++) {
+	    addr[i] = simple_strtoul(s + 1, &s, 0x10);
+	    if (*s != (i==5?')' : ':' ))
+		break;
+	}
+	if (i == 6)
+	    return;
+    }
+    memcpy (addr, def_eth_addr, 6);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static int
+ether3_ramtest(struct device *dev, unsigned char byte)
+{
+    unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL);
+    unsigned long flags;
+    int i,ret=0;
+    int max_errors = 4;
+    int bad=-1;
+
+    if (!buffer)
+	return 1;
+
+    save_flags (flags);
+    cli ();
+
+    memset (buffer, byte, RX_END);
+    ether3_writebuffer (dev, buffer, 0, TX_END);
+    ether3_writebuffer (dev, buffer+RX_START, RX_START, RX_LEN);
+
+    memset (buffer, byte ^ 0xff, RX_END);
+    ether3_readbuffer (dev, buffer, 0, TX_END);
+    ether3_readbuffer (dev, buffer+RX_START, RX_START, RX_LEN);
+
+    for (i=0; i<RX_END; i++) {
+	if (buffer[i] != byte) {
+	    if (max_errors>=0 && bad!=buffer[i]) {
+		printk ("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
+			dev->name, buffer[i], byte, i);
+		ret = 2;
+		max_errors--;
+		bad = buffer[i];
+	    }
+	} else {
+	    if (bad != -1) {
+		printk(" - 0x%04X\n", i);
+		bad = -1;
+	    }
+	}
+    }
+    if (bad != -1)
+	printk (" - 0x10000\n");
+    kfree (buffer);
+    restore_flags (flags);
+
+    return ret;
+}
+
+/* ------------------------------------------------------------------------------- */
+
+static int
+ether3_init_2(struct device *dev)
+{
+    FUNC_PROLOGUE;
+    int i;
+    unsigned long txhdr = 0;
+
+    priv->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
+    priv->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
+    priv->regs.command = 0;
+    /*
+     * Set up our hardware address
+     */
+    outw (priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+    for (i = 0; i < 6; i++)
+	outb (dev->dev_addr[i], REG_BUFWIN);
+
+    if (dev->flags & IFF_PROMISC)
+	priv->regs.config1 |= CFG1_RECVPROMISC;
+    else
+    if (dev->flags & IFF_MULTICAST)
+	priv->regs.config1 |= CFG1_RECVSPECBRMULTI;
+    else
+	priv->regs.config1 |= CFG1_RECVSPECBROAD;
+
+    /*
+     * There is a problem with the NQ8005 in that it occasionally losses the
+     * last two bytes.  To get round this problem, we receive the CRC as well.
+     * That way, if we do loose the last two, then it doesn't matter
+     */
+    outw (priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+    outw ((TX_END>>8) - 1, REG_BUFWIN);
+    outw (priv->regs.recvptr, REG_RECVPTR);
+    outw (priv->regs.transmitptr, REG_TRANSMITPTR);
+    outw (priv->regs.recvptr >> 8, REG_RECVEND);
+    outw (priv->regs.config2, REG_CONFIG2);
+    outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+
+    ether3_writebuffer (dev, &txhdr, priv->regs.transmitptr, 4);
+
+    outw (priv->regs.command, REG_COMMAND);
+
+    i = ether3_ramtest (dev, 0x5A);
+    if(i)
+	return i;
+    return ether3_ramtest (dev, 0x1E);
+}
+
+static void
+ether3_init_for_open(struct device *dev)
+{
+    FUNC_PROLOGUE;
+    unsigned long txhdr = 0;
+    int i;
+
+    memset (&priv->stats, 0, sizeof (struct enet_statistics));
+
+    priv->regs.command = 0;
+    priv->txbuffered = 0;
+    outw (CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+    while (inw (REG_STATUS) & (STAT_RXON|STAT_TXON));
+
+    outw (priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+    for (i = 0; i < 6; i++)
+	outb (dev->dev_addr[i], REG_BUFWIN);
+
+    priv->regs.transmitptr = 0;	/* address that transmitter has processed to */
+    priv->txinsert = 0;		/* address of next available packet for tx */
+    priv->txed = 0;			/* transmitted length index */
+    priv->txto = 0;			/* to transmit length index */
+    priv->tx_ends[0] = 0;
+    priv->use = 0;
+
+    priv->regs.config2 |= CFG2_CTRLO;
+
+    outw (priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+    outw ((TX_END>>8) - 1, REG_BUFWIN);
+
+    priv->regs.recvptr	= RX_START;
+    outw (priv->regs.recvptr, REG_RECVPTR);
+    outw (priv->regs.recvptr >> 8, REG_RECVEND);
+
+    outw (0, REG_TRANSMITPTR);
+    outw (priv->regs.config2, REG_CONFIG2);
+    outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+
+    ether3_writebuffer (dev, &txhdr, priv->regs.transmitptr, 4);
+
+    priv->regs.command = CMD_ENINTRX|CMD_ENINTTX;
+    outw (priv->regs.command | CMD_RXON, REG_COMMAND);
+}
+
+/*
+ * This is the real probe routine.
+ */
+static int
+ether3_probe1(struct device *dev)
+{
+    static unsigned version_printed = 0;
+    struct dev_priv *priv;
+    int i;
+
+    if (!dev->priv)
+	dev->priv = kmalloc (sizeof (struct dev_priv), GFP_KERNEL);
+
+    if (!dev->priv)
+	return -ENOMEM;
+
+    priv = (struct dev_priv *) dev->priv;
+    memset (priv, 0, sizeof(struct dev_priv));
+    priv->broken = 0;
+
+    outb (0x80, REG_CONFIG2 + 1);
+    outb (0, REG_COMMAND);
+
+    /*
+     * test to see if we have an 8 or 16-bit IO bus
+     */
+    outb (1, REG_CONFIG1);
+    if (inb (REG_CONFIG1 + 1) == 1 && inb (REG_CONFIG1) == 0)
+        priv->bus_type = BUS_8;
+    else
+    if (inw (REG_CONFIG1) == 0x0101)
+        priv->bus_type = BUS_16;
+    else {
+	kfree(dev->priv);
+	dev->priv = NULL;
+	return -ENODEV;
+    }
+
+    if (net_debug  &&  version_printed++ == 0)
+	printk(version);
+
+    printk("%s: ether3 found [%d, %04lx, %d] ", dev->name, priv->bus_type,
+	dev->base_addr, dev->irq);
+
+    if (priv->bus_type == BUS_8) {
+	printk ("*** 8-bit not supported (yet) ***\n");
+	kfree (dev->priv);
+	dev->priv = NULL;
+	return -ENODEV;
+    }
+
+    /* Retrive and print the ethernet address. */
+    for (i = 0; i < 6; i++)
+	printk (i == 5 ? "%2.2x\n" : "%2.2x:", dev->dev_addr[i]);
+
+    if (ether3_init_2(dev)) {
+	kfree (dev->priv);
+	dev->priv = NULL;
+	return -ENODEV;
+    }
+
+    dev->open = ether3_open;
+    dev->stop = ether3_close;
+    dev->hard_start_xmit = ether3_send_packet;
+    dev->get_stats = ether3_get_stats;
+    dev->set_multicast_list = set_multicast_list;
+
+    /* Fill in the fields of the device structure with ethernet values. */
+    ether_setup(dev);
+#ifndef CLAIM_IRQ_AT_OPEN
+    if (request_irq(dev->irq, ether3_interrupt, 0, "ether3")) {
+	kfree (dev->priv);
+	dev->priv = NULL;
+	return -EAGAIN;
+    }
+
+    irq2dev_map[dev->irq] = dev;
+#endif
+    return 0;
+}
+
+#ifndef MODULE
+int
+ether3_probe(struct device *dev)
+{
+    struct expansion_card *ec;
+
+    if (!dev)
+	return ENODEV;
+
+    if ((ec = ecard_find (0, sizeof(ether3_prods), ether3_prods, ether3_manus)) == NULL)
+	return ENODEV;
+
+    dev->base_addr = ((unsigned long)ec->r_podaddr & ~0x003c0000UL) >> 2;
+    dev->irq = ec->irq;
+
+    ecard_claim (ec);
+
+    ether3_addr (dev->dev_addr, ec);
+    return ether3_probe1(dev);
+}
+#endif
+
+/*
+ * Open/initialize the board.  This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int
+ether3_open(struct device *dev)
+{
+    ether3_init_for_open(dev);
+
+#ifdef CLAIM_IRQ_AT_OPEN
+    if (request_irq(dev->irq, ether3_interrupt, 0, "ether3"))
+	return -EAGAIN;
+
+    irq2dev_map[dev->irq] = dev;
+#endif
+
+    MOD_INC_USE_COUNT;
+
+    dev->tbusy = 0;
+    dev->interrupt = 0;
+    dev->start = 1;
+    return 0;
+}
+
+/*
+ * The inverse routine to ether3_open().
+ */
+static int
+ether3_close(struct device *dev)
+{
+    FUNC_PROLOGUE;
+    unsigned long flags;
+
+    dev->tbusy = 1;
+    dev->start = 0;
+
+    save_flags (flags);
+    cli ();
+
+    outw (CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+    priv->regs.command = 0;
+    while (inw (REG_STATUS) & (STAT_RXON|STAT_TXON));
+    outb (0x80, REG_CONFIG2 + 1);
+    outw (0, REG_COMMAND);
+
+    restore_flags (flags);
+#ifdef CLAIM_IRQ_AT_OPEN
+    free_irq (dev->irq);
+
+    irq2dev_map[dev->irq] = NULL;
+#endif
+
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+/*
+ * Get the current statistics.	This may be called with the card open or
+ * closed.
+ */
+static struct enet_statistics *
+ether3_get_stats(struct device *dev)
+{
+    FUNC_PROLOGUE;
+    return &priv->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+ *			best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+    FUNC_PROLOGUE;
+
+    priv->regs.config1 &= ~CFG1_RECVPROMISC;
+	
+    switch (num_addrs) {
+	case -1:	priv->regs.config1 |= CFG1_RECVPROMISC;     break;
+	case 0:		priv->regs.config1 |= CFG1_RECVSPECBROAD;   break;
+	default:	priv->regs.config1 |= CFG1_RECVSPECBRMULTI; break;
+    }
+
+    outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+}
+
+/*
+ * Transmit a packet
+ */
+static int
+ether3_send_packet(struct sk_buff *skb, struct device *dev)
+{
+    FUNC_PROLOGUE;
+
+    if (dev->tbusy) {
+	/* If we get here, some higher level has decided we are broken.
+	   There should really be a "kick me" function call instead. */
+	int tickssofar = jiffies - dev->trans_start;
+	if (tickssofar < 5)
+	    return 1;
+	printk("%s: transmit timed out, network cable problem?\n", dev->name);
+	/* Try to restart the adaptor. */
+	dev->tbusy = 0;
+	priv->use = 0;
+	priv->regs.config2 |= CFG2_CTRLO;
+	outw (priv->regs.config2 , REG_CONFIG2);
+	dev->trans_start = jiffies;
+    }
+
+    /* If some higher layer thinks we've missed an tx-done interrupt
+       we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+       itself. */
+    if (skb == NULL) {
+	dev_tint(dev);
+	return 0;
+    }
+
+    /* Block a timer-based transmit from overlapping.  This could better be
+       done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+    if (set_bit(0, (void*)&dev->tbusy) != 0)
+	printk("%s: Transmitter access conflict.\n", dev->name);
+    else {
+	unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	unsigned int thisdata, thisstart, nextpacket;
+	unsigned long flags;
+	unsigned long thishdr = TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS
+				|TXHDR_ENSUCCESS;
+	unsigned long nexthdr = 0;
+
+	if (priv->broken) {
+	    dev_kfree_skb(skb, FREE_WRITE);
+	    priv->stats.tx_dropped ++;
+	    return 1;
+	}
+
+	thisstart = priv->txinsert;
+	thisdata  = thisstart + 4;
+	if (thisdata >= TX_END)
+	    thisdata -= TX_END;
+
+	nextpacket = thisdata + length;
+	if (nextpacket >= TX_END)
+	    nextpacket -= TX_END;
+
+	priv->txinsert = nextpacket;
+	priv->tx_ends[priv->txto++] = nextpacket;
+
+	if (priv->txto >= MAX_TXED)
+	    priv->txto = 0;
+
+	thishdr |= htons (nextpacket);
+
+	save_flags(flags);
+	cli();
+	ether3_ledincrementuse (dev);
+	ether3_writebuffer(dev, skb->data, thisdata, length&1?(length+1):length);
+	ether3_writebuffer(dev, &nexthdr, -1, 4);
+	ether3_writebuffer(dev, &thishdr, thisstart, 4);
+
+	if ((inw (REG_STATUS) & STAT_TXON) == 0) {
+	    outw (thisstart, REG_TRANSMITPTR);
+	    outw (priv->regs.command | CMD_TXON, REG_COMMAND);
+	}
+	restore_flags(flags);
+
+	dev->trans_start = jiffies;
+	if (priv->txbuffered ++ < MAX_TX_BUFFERED)
+	    dev->tbusy = 0;
+    }
+    dev_kfree_skb (skb, FREE_WRITE);
+
+    return 0;
+}
+
+static void
+ether3_interrupt(int irq, struct pt_regs *regs)
+{
+    struct device *dev = (struct device *)(irq2dev_map[irq]);
+    struct dev_priv *priv;
+    int boguscount = 20, done = 0;
+
+#if NET_DEBUG > 1
+    if(net_debug & DEBUG_INT)
+	printk("ether3_interrupt: irq %d\n", irq);
+#endif
+
+    if (dev == NULL) {
+	printk ("ether3_interrupt(): irq %d for unknown device.\n", irq);
+	return;
+    }
+
+    priv = (struct dev_priv *)dev->priv;
+
+    dev->interrupt = 1;
+
+    do {
+    	unsigned int status;
+	status = inw(REG_STATUS);
+	outw ((status & (STAT_INTRX|STAT_INTTX|STAT_INTBUFWIN))
+		| priv->regs.command, REG_COMMAND);
+
+	if (status & STAT_INTRX) { /* Got a packet(s). */
+	    ether3_rx (dev, priv);
+	    done = 1;
+	}
+	if (status & STAT_INTTX) { /* Packets transmitted */
+	    ether3_tx (dev, priv);
+	    done = 1;
+	}
+    } while (-- boguscount && !done) ;
+
+    dev->interrupt = 0;
+
+#if NET_DEBUG > 1
+    if(net_debug & DEBUG_INT)
+	printk("ether3_interrupt: done\n", irq);
+#endif
+}
+
+/*
+ * If we have a good packet(s), get it/them out of the buffers.
+ */
+static void
+ether3_rx(struct device *dev, struct dev_priv *priv)
+{
+    unsigned int current_recv_ptr;
+    unsigned int maxcnt = 10;
+
+    ether3_ledincrementuse (dev);
+
+    current_recv_ptr = priv->regs.recvptr;
+
+    do {
+    	struct t {
+    	    unsigned long rxhdr;
+    	    unsigned char dst_addr[6];
+    	    unsigned char src_addr[6];
+    	} p;
+	unsigned char *buf;
+	unsigned int prev_recv_ptr;
+	unsigned int length;
+	struct sk_buff *skb;
+
+	/*
+	 * read the first 16 bytes from the buffer.
+	 * This contains the status bytes etc and ethernet addresses,
+	 * and we also check the source ethernet address to see if
+	 * it origionated from us.
+	 */
+	prev_recv_ptr = current_recv_ptr;
+	ether3_readbuffer (dev, &p, current_recv_ptr, 16);
+
+	if ((p.rxhdr & RX_NEXT) == 0 || (p.rxhdr & RXSTAT_DONE) == 0
+		|| (p.rxhdr & (RXHDR_CHAINCONTINUE|RXHDR_RECEIVE)) != RXHDR_CHAINCONTINUE)
+	    break;
+
+	current_recv_ptr = ntohs (p.rxhdr & RX_NEXT);
+
+	/*
+ 	 * ignore our own packets...
+ 	 */
+	if (memcmp (dev->dev_addr, &p.src_addr, 6) == 0) {
+	    outw (current_recv_ptr >> 8, REG_RECVEND);
+	    continue;
+	}
+
+	if (p.rxhdr & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET)) {
+	    priv->stats.rx_errors++;
+	    if(p.rxhdr & RXSTAT_OVERSIZE) priv->stats.rx_length_errors ++;
+	    if(p.rxhdr & RXSTAT_CRCERROR) priv->stats.rx_crc_errors ++;
+	    if(p.rxhdr & RXSTAT_DRIBBLEERROR) priv->stats.rx_fifo_errors ++;
+	    if(p.rxhdr & RXSTAT_SHORTPACKET) priv->stats.rx_length_errors ++;
+	    outw (current_recv_ptr >> 8, REG_RECVEND);
+	    continue;
+	}
+
+	if (current_recv_ptr > prev_recv_ptr)
+	    length = current_recv_ptr - prev_recv_ptr - 4;
+	else
+	    length = (current_recv_ptr - RX_START) + (RX_END - prev_recv_ptr) - 4;
+
+	skb = dev_alloc_skb(length + 2);
+	if (skb == NULL) {
+	    printk("%s: memory squeeze, dropping packet.\n", dev->name);
+	    priv->stats.rx_dropped ++;
+	    break;
+	}
+
+	skb->dev = dev;
+	skb_reserve (skb, 2);
+	buf = skb_put (skb, length);
+	/*
+	 * buf isn't word aligned, and it is probably faster to
+	 * copy the address this way
+	 */
+	*(unsigned short *)&buf[0] = *(unsigned short *)&p.dst_addr[0];
+	*(unsigned short *)&buf[2] = *(unsigned short *)&p.dst_addr[2];
+	*(unsigned short *)&buf[4] = *(unsigned short *)&p.dst_addr[4];
+	*(unsigned short *)&buf[6] = *(unsigned short *)&p.src_addr[0];
+	*(unsigned short *)&buf[8] = *(unsigned short *)&p.src_addr[2];
+	*(unsigned short *)&buf[10] = *(unsigned short *)&p.src_addr[4];
+	
+	ether3_readbuffer (dev, buf + 12, -1, length - 12);
+
+	outw (current_recv_ptr >> 8, REG_RECVEND);
+
+	skb->protocol = eth_type_trans(skb, dev);
+	netif_rx (skb);
+	priv->stats.rx_packets++;
+    }
+    while (-- maxcnt);
+
+    priv->regs.recvptr = current_recv_ptr;
+    if (!(inw (REG_STATUS) & STAT_RXON)) {
+    	outw (priv->regs.recvptr, REG_RECVPTR);
+	outw (priv->regs.command | CMD_RXON, REG_COMMAND);
+    }
+
+    /* If any worth-while packets have been received, dev_rint()
+       has done a mark_bh(NET_BH) for us and will work on them
+       when we get to the bottom-half routine. */
+
+    ether3_leddecrementuse (dev);
+}
+
+/*
+ * Update stats for the transmitted packet
+ */
+static void
+ether3_tx(struct device *dev, struct dev_priv *priv)
+{
+    unsigned long txhdr;
+    unsigned char *buffer = (unsigned char *)&txhdr;
+    unsigned int txptr;
+
+    txptr = priv->regs.transmitptr;
+
+    do {
+    	/*
+    	 * Read the packet header
+    	 */
+	if (txptr + 4 > TX_END) {
+	    int len = TX_END - priv->regs.transmitptr;
+	    if (len)
+		ether3_readbuffer (dev, buffer, txptr, len);
+	    ether3_readbuffer (dev, buffer + len, 0, 4 - len);
+	} else
+	    ether3_readbuffer (dev, buffer, txptr, 4);
+
+	/*
+	 * Check to see if this packet has been transmitted
+	 */
+	if (!(txhdr & TXSTAT_DONE) || !(txhdr & TXHDR_TRANSMIT)) {
+	    priv->regs.transmitptr = txptr;
+	    return;
+	}
+
+	/*
+	 * Update collision count (undocumented in datasheet)
+	 */
+	if (txhdr & TXSTAT_COLLISION)
+	    priv->stats.collisions += (txhdr >> (3+24)) & 15;
+
+	/*
+	 * Update errors
+	 */
+	if (txhdr & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS)) {
+	    priv->stats.tx_errors ++;
+	    if (txhdr & TXSTAT_16COLLISIONS) priv->stats.collisions += 16;
+	    if (txhdr & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++;
+	} else
+	    priv->stats.tx_packets++;
+
+	/*
+	 * Get next packet address
+	 */
+	txptr = priv->tx_ends[priv->txed++]; /* next packet address */
+
+	if (priv->txed >= MAX_TXED)
+	    priv->txed = 0;
+
+	if (txptr != ntohs (txhdr & TX_NEXT)) {
+	    printk ("%s: TX: bad next pointer - %04X instead of %04X\n",
+		dev->name, ntohs (txhdr & TX_NEXT), txptr);
+	    return;
+	}
+
+	if (priv->regs.transmitptr >= TX_END)
+	    priv->regs.transmitptr -= TX_END;
+
+	if (priv->txbuffered)
+	    priv->txbuffered -= 1;
+
+	dev->tbusy = 0;
+
+	ether3_leddecrementuse (dev);
+
+	mark_bh (NET_BH);	/* Inform upper layers. */
+    } while (1);
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+char *ethernames[4] = {
+    "        ",
+    "        ",
+    "        ",
+    "        "
+};
+
+static struct device *my_ethers[4];
+static struct expansion_card *ec[4];
+
+int
+init_module(void)
+{
+    int i;
+
+    for(i = 0; i < 4; i++) {
+	my_ethers[i] = NULL;
+	ec[i] = NULL;
+    }
+
+    i = 0;
+
+    do {
+	if ((ec[i] = ecard_find(0, sizeof(ether3_prods), ether3_prods, ether3_manus)) == NULL)
+	    break;
+
+	my_ethers[i] = (struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
+	memset(my_ethers[i], 0, sizeof(struct device));
+
+	my_ethers[i]->irq = ec[i]->irq;
+	my_ethers[i]->base_addr= ((unsigned long)ec[i]->r_podaddr & ~0x003c0000UL)>>2;
+	my_ethers[i]->init = ether3_probe1;
+	my_ethers[i]->name = ethernames[i];
+
+	ether3_addr(my_ethers[i]->dev_addr, ec[i]);
+
+	ecard_claim(ec[i]);
+
+	if(register_netdev(my_ethers[i]) != 0) {
+	    for (i = 0; i < 4; i++) {
+		if(my_ethers[i]) {
+		    kfree(my_ethers[i]);
+		    my_ethers[i] = NULL;
+		}
+		if(ec[i]) {
+		    ecard_release(ec[i]);
+		    ec[i] = NULL;
+		}
+	    }
+	    return -EIO;
+	}
+	i++;
+    }
+    while(i < 4);
+
+    return i != 0 ? 0 : -ENODEV;
+}
+
+void
+cleanup_module(void)
+{
+    if (MOD_IN_USE) {
+	printk("ether3: device busy, remove delayed\n");
+    } else {
+	int i;
+	for (i = 0; i < 4; i++) {
+	    if (my_ethers[i]) {
+		unregister_netdev(my_ethers[i]);
+		my_ethers[i] = NULL;
+	    }
+	    if (ec[i]) {
+		ecard_release(ec[i]);
+		ec[i] = NULL;
+	    }
+	}
+    }
+}
+#endif /* MODULE */
diff -urNwbB linux/arch/arm/drivers/net/ether3.h linux.arm/arch/arm/drivers/net/ether3.h
--- linux/arch/arm/drivers/net/ether3.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ether3.h	Sat Mar  2 13:07:27 1996
@@ -0,0 +1,174 @@
+/*
+ * linux/drivers/net/ether3.h
+ *
+ * network driver for Acorn Ether3 cards
+ */
+
+#ifndef _LINUX_ether3_H
+#define _LINUX_ether3_H
+
+/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
+#define DEBUG_TX	 2
+#define DEBUG_RX	 4
+#define DEBUG_INT	 8
+#define DEBUG_IC	16
+#ifndef NET_DEBUG
+#define NET_DEBUG 	0
+#endif
+
+/* Command register definitions & bits */
+#define REG_COMMAND		(dev->base_addr + 0x00)
+#define CMD_ENINTDMA		0x0001
+#define CMD_ENINTRX		0x0002
+#define CMD_ENINTTX		0x0004
+#define CMD_ENINTBUFWIN		0x0008
+#define CMD_ACKINTDMA		0x0010
+#define CMD_ACKINTRX		0x0020
+#define CMD_ACKINTTX		0x0040
+#define CMD_ACKINTBUFWIN	0x0080
+#define CMD_DMAON		0x0100
+#define CMD_RXON		0x0200
+#define CMD_TXON		0x0400
+#define CMD_DMAOFF		0x0800
+#define CMD_RXOFF		0x1000
+#define CMD_TXOFF		0x2000
+#define CMD_FIFOREAD		0x4000
+#define CMD_FIFOWRITE		0x8000
+
+/* status register */
+#define REG_STATUS		(dev->base_addr + 0x00)
+#define STAT_ENINTSTAT		0x0001
+#define STAT_ENINTRX		0x0002
+#define STAT_ENINTTX		0x0004
+#define STAT_ENINTBUFWIN	0x0008
+#define STAT_INTDMA		0x0010
+#define STAT_INTRX		0x0020
+#define STAT_INTTX		0x0040
+#define STAT_INTBUFWIN		0x0080
+#define STAT_DMAON		0x0100
+#define STAT_RXON		0x0200
+#define STAT_TXON		0x0400
+#define STAT_FIFOFULL		0x2000
+#define STAT_FIFOEMPTY		0x4000
+#define STAT_FIFODIR		0x8000
+
+/* configuration register 1 */
+#define REG_CONFIG1		(dev->base_addr + 0x10)
+#define CFG1_BUFSELSTAT0	0x0000
+#define CFG1_BUFSELSTAT1	0x0001
+#define CFG1_BUFSELSTAT2	0x0002
+#define CFG1_BUFSELSTAT3	0x0003
+#define CFG1_BUFSELSTAT4	0x0004
+#define CFG1_BUFSELSTAT5	0x0005
+#define CFG1_ADDRPROM		0x0006
+#define CFG1_TRANSEND		0x0007
+#define CFG1_LOCBUFMEM		0x0008
+#define CFG1_INTVECTOR		0x0009
+#define CFG1_DMABURSTCONT	0x0000
+#define CFG1_DMABURST800NS	0x0010
+#define CFG1_DMABURST1600NS	0x0020
+#define CFG1_DMABURST3200NS	0x0030
+#define CFG1_DMABURST1		0x0000
+#define CFG1_DMABURST4		0x0040
+#define CFG1_DMABURST8		0x0080
+#define CFG1_DMABURST16		0x00C0
+#define CFG1_RECVCOMPSTAT0	0x0100
+#define CFG1_RECVCOMPSTAT1	0x0200
+#define CFG1_RECVCOMPSTAT2	0x0400
+#define CFG1_RECVCOMPSTAT3	0x0800
+#define CFG1_RECVCOMPSTAT4	0x1000
+#define CFG1_RECVCOMPSTAT5	0x2000
+#define CFG1_RECVSPECONLY	0x0000
+#define CFG1_RECVSPECBROAD	0x4000
+#define CFG1_RECVSPECBRMULTI	0x8000
+#define CFG1_RECVPROMISC	0xC000
+
+/* configuration register 2 */
+#define REG_CONFIG2		(dev->base_addr + 0x20)
+#define CFG2_BYTESWAP		0x0001
+#define CFG2_ERRENCRC		0x0008
+#define CFG2_ERRENDRIBBLE	0x0010
+#define CFG2_ERRSHORTFRAME	0x0020
+#define CFG2_SLOTSELECT		0x0040
+#define CFG2_PREAMSELECT	0x0080
+#define CFG2_ADDRLENGTH		0x0100
+#define CFG2_RECVCRC		0x0200
+#define CFG2_XMITNOCRC		0x0400
+#define CFG2_LOOPBACK		0x0800
+#define CFG2_CTRLO		0x1000
+#define CFG2_RESET		0x8000
+
+#define REG_RECVEND		(dev->base_addr + 0x30)
+
+#define REG_BUFWIN		(dev->base_addr + 0x40)
+
+#define REG_RECVPTR		(dev->base_addr + 0x50)
+
+#define REG_TRANSMITPTR		(dev->base_addr + 0x60)
+
+#define REG_DMAADDR		(dev->base_addr + 0x70)
+
+/*
+ * Cards transmit/receive headers
+ */
+#define TX_NEXT			(0xffff)
+#define TXHDR_ENBABBLEINT	(1 << 16)
+#define TXHDR_ENCOLLISIONINT	(1 << 17)
+#define TXHDR_EN16COLLISION	(1 << 18)
+#define TXHDR_ENSUCCESS		(1 << 19)
+#define TXHDR_DATAFOLLOWS	(1 << 21)
+#define TXHDR_CHAINCONTINUE	(1 << 22)
+#define TXHDR_TRANSMIT		(1 << 23)
+#define TXSTAT_BABBLED		(1 << 24)
+#define TXSTAT_COLLISION	(1 << 25)
+#define TXSTAT_16COLLISIONS	(1 << 26)
+#define TXSTAT_DONE		(1 << 31)
+
+#define RX_NEXT			(0xffff)
+#define RXHDR_CHAINCONTINUE	(1 << 22)
+#define RXHDR_RECEIVE		(1 << 23)
+#define RXSTAT_OVERSIZE		(1 << 24)
+#define RXSTAT_CRCERROR		(1 << 25)
+#define RXSTAT_DRIBBLEERROR	(1 << 26)
+#define RXSTAT_SHORTPACKET	(1 << 27)
+#define RXSTAT_DONE		(1 << 31)
+
+
+#define TX_END          0x6000
+#define RX_START        0x6000
+#define RX_LEN          0xA000
+#define RX_END          0x10000
+#define MAX_TXED	360
+#define MAX_TX_BUFFERED	10
+
+struct dev_priv {
+    int bus_type;			/* type of bus (not currently used)	 */
+    struct {
+	unsigned int command;
+	unsigned int config1;
+	unsigned int config2;
+	unsigned int transmitptr;
+	unsigned int recvptr;
+    } regs;
+    struct enet_statistics stats;
+    unsigned int txinsert;		/* address to insert next packet	 */
+    unsigned int txed;			/* transmitted index in tx_ends		 */
+    unsigned int txto;			/* to transmit index in tx_ends		 */
+    unsigned int txbuffered;		/* number of packets currently buffered	 */
+    unsigned int tx_ends[MAX_TXED];	/* addresses of transmit packets	 */
+    unsigned int use;			/* 0 = card not in use, >0 packet transferring */
+    int broken;				/* 0 = ok, 1 = something went wrong	 */
+};
+
+extern int	ether3_probe(struct device *dev);
+static int	ether3_probe1(struct device *dev);
+static int	ether3_open(struct device *dev);
+static int	ether3_send_packet(struct sk_buff *skb, struct device *dev);
+static void	ether3_interrupt(int irq, struct pt_regs *regs);
+static void	ether3_rx(struct device *dev, struct dev_priv *priv);
+static void	ether3_tx(struct device *dev, struct dev_priv *priv);
+static int	ether3_close(struct device *dev);
+static struct enet_statistics *ether3_get_stats(struct device *dev);
+static void	set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+
+#endif
diff -urNwbB linux/arch/arm/drivers/net/plip.c linux.arm/arch/arm/drivers/net/plip.c
--- linux/arch/arm/drivers/net/plip.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/plip.c	Sat Feb 24 09:36:44 1996
@@ -0,0 +1,1272 @@
+/* $Id: plip.c,v 1.15 1995/10/03 01:47:09 gniibe Exp $ */
+/* PLIP: A parallel port "network" driver for Linux. */
+/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
+/*
+ * Authors:	Donald Becker,  <becker@super.org>
+ *		Tommy Thorn, <thorn@daimi.aau.dk>
+ *		Tanabe Hiroyasu, <hiro@sanpo.t.u-tokyo.ac.jp>
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Peter Bauer, <100136.3530@compuserve.com>
+ *		Niibe Yutaka, <gniibe@mri.co.jp>
+ *
+ *		Modularization and ifreq/ifmap support by Alan Cox.
+ *		Rewritten by Niibe Yutaka.
+ *
+ * Fixes:
+ *		9-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
+ *		  - only claim 3 bytes of I/O space for port at 0x3bc
+ *		  - treat NULL return from register_netdev() as success in
+ *		    init_module()
+ *		  - added message if driver loaded as a module but no
+ *		    interfaces present.
+ *		  - release claimed I/O ports if malloc() fails during init.
+ *		
+ *		Niibe Yutaka
+ *		  - Module initialization.  You can specify I/O addr and IRQ:
+ *			# insmod plip.o io=0x3bc irq=7
+ *		  - MTU fix.
+ *		  - Make sure other end is OK, before sending a packet.
+ *
+ *		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.
+ */
+
+/*
+ * Original version and the name 'PLIP' from Donald Becker <becker@super.org>
+ * inspired by Russ Nelson's parallel port packet driver.
+ *
+ * NOTE:
+ *     Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0.
+ *     Because of the necessity to communicate to DOS machines with the
+ *     Crynwr packet driver, Peter Bauer changed the protocol again
+ *     back to original protocol.
+ *
+ *     This version follows original PLIP protocol. 
+ *     So, this PLIP can't communicate the PLIP of Linux v1.0.
+ */
+
+/*
+ *     To use with DOS box, please do (Turn on ARP switch):
+ *	# ifconfig plip[0-2] arp
+ */
+static const char *version = "NET3 PLIP version 2.1 gniibe@mri.co.jp\n";
+
+/*
+  Sources:
+	Ideas and protocols came from Russ Nelson's <nelson@crynwr.com>
+	"parallel.asm" parallel port packet driver.
+
+  The "Crynwr" parallel port standard specifies the following protocol:
+    Trigger by sending '0x08' (this cause interrupt on other end)
+    count-low octet
+    count-high octet
+    ... data octets
+    checksum octet
+  Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
+			<wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>
+
+  The packet is encapsulated as if it were ethernet.
+
+  The cable used is a de facto standard parallel null cable -- sold as
+  a "LapLink" cable by various places.  You'll need a 12-conductor cable to
+  make one yourself.  The wiring is:
+    SLCTIN	17 - 17
+    GROUND	25 - 25
+    D0->ERROR	2 - 15		15 - 2
+    D1->SLCT	3 - 13		13 - 3
+    D2->PAPOUT	4 - 12		12 - 4
+    D3->ACK	5 - 10		10 - 5
+    D4->BUSY	6 - 11		11 - 6
+  Do not connect the other pins.  They are
+    D5,D6,D7 are 7,8,9
+    STROBE is 1, FEED is 14, INIT is 16
+    extra grounds are 18,19,20,21,22,23,24
+*/
+
+#define NO_IRQ 255
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/if_ether.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/lp.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_plip.h>
+
+#include <linux/tqueue.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+
+/* Use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* In micro second */
+#define PLIP_DELAY_UNIT		   1
+
+/* Connection time out = PLIP_TRIGGER_WAIT * PLIP_DELAY_UNIT usec */
+#define PLIP_TRIGGER_WAIT	 500
+
+/* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
+#define PLIP_NIBBLE_WAIT        3000
+
+#define PAR_INTR_ON		(LP_PINITP|LP_PSELECP|LP_PINTEN)
+#define PAR_INTR_OFF		(LP_PINITP|LP_PSELECP)
+#define PAR_DATA(dev)		((dev)->base_addr+0)
+#define PAR_STATUS(dev)		((dev)->base_addr+1)
+#define PAR_CONTROL(dev)	((dev)->base_addr+2)
+
+/* Bottom halfs */
+static void plip_kick_bh(struct device *dev);
+static void plip_bh(struct device *dev);
+
+/* Interrupt handler */
+static void plip_interrupt(int irq, struct pt_regs *regs);
+
+/* Functions for DEV methods */
+static int plip_rebuild_header(void *buff, struct device *dev,
+			       unsigned long raddr, struct sk_buff *skb);
+static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
+static int plip_open(struct device *dev);
+static int plip_close(struct device *dev);
+static struct enet_statistics *plip_get_stats(struct device *dev);
+static int plip_config(struct device *dev, struct ifmap *map);
+static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+
+enum plip_connection_state {
+	PLIP_CN_NONE=0,
+	PLIP_CN_RECEIVE,
+	PLIP_CN_SEND,
+	PLIP_CN_CLOSING,
+	PLIP_CN_ERROR
+};
+
+enum plip_packet_state {
+	PLIP_PK_DONE=0,
+	PLIP_PK_TRIGGER,
+	PLIP_PK_LENGTH_LSB,
+	PLIP_PK_LENGTH_MSB,
+	PLIP_PK_DATA,
+	PLIP_PK_CHECKSUM
+};
+
+enum plip_nibble_state {
+	PLIP_NB_BEGIN,
+	PLIP_NB_1,
+	PLIP_NB_2,
+};
+
+struct plip_local {
+	enum plip_packet_state state;
+	enum plip_nibble_state nibble;
+	union {
+		struct {
+#if defined(__LITTLE_ENDIAN)
+			unsigned char lsb;
+			unsigned char msb;
+#elif defined(__BIG_ENDIAN)
+			unsigned char msb;
+			unsigned char lsb;
+#else
+#error	"Please fix the endianness defines in <asm/byteorder.h>"
+#endif						
+		} b;
+		unsigned short h;
+	} length;
+	unsigned short byte;
+	unsigned char  checksum;
+	unsigned char  data;
+	struct sk_buff *skb;
+};
+
+struct net_local {
+	struct enet_statistics enet_stats;
+	struct tq_struct immediate;
+	struct tq_struct deferred;
+	struct plip_local snd_data;
+	struct plip_local rcv_data;
+	unsigned long  trigger;
+	unsigned long  nibble;
+	enum plip_connection_state connection;
+	unsigned short timeout_count;
+	char is_deferred;
+	char iostate;
+	int (*orig_rebuild_header)(void *eth, struct device *dev,
+				   unsigned long raddr, struct sk_buff *skb);
+};
+
+#define IOSTATE(x) ((x)->iostate)
+#define RACK_STATE	0x03
+#define RACK_UNKNOWN	0x00
+#define RACK_ACTIVEHIGH	0x01
+#define RACK_ACTIVELOW	0x02
+
+#define TACK_HIGH	0x10
+#define TACK_UNKNOWN	0x20
+
+#define RESET_INTERRUPT ({ int rstirq = 0x3350058 >> 2; (void)inb (rstirq); })
+
+
+/* Entry point of PLIP driver.
+   Probe the hardware, and register/initialize the driver. */
+int
+plip_init(struct device *dev)
+{
+	struct net_local *nl;
+	int iosize = (PAR_DATA(dev) == 0x3bc) ? 3 : 8;
+
+	/* Check region before the probe */
+	if (check_region(PAR_DATA(dev), iosize) < 0)
+		return -ENODEV;
+
+	/* Check that there is something at base_addr. */
+	outb(0, PAR_DATA(dev));
+	udelay(1000);
+	if (inb(PAR_DATA(dev)) != 0)
+		return -ENODEV;
+
+	printk(version);
+	printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
+	if (dev->irq != NO_IRQ) {
+		printk("using assigned IRQ %d.\n", dev->irq);
+	} else {
+		int irq = NO_IRQ;
+#ifdef MODULE
+		/* dev->irq==NO_IRQ means autoprobe, but we don't try to do so
+		   with module.  We can change it by ifconfig */
+#else
+		unsigned int irqs = probe_irq_on();
+
+		outb(0x00, PAR_CONTROL(dev));
+		udelay(1000);
+		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+		outb(PAR_INTR_ON, PAR_CONTROL(dev));
+		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+		udelay(1000);
+		irq = probe_irq_off(irqs);
+#endif
+		if (irq != NO_IRQ) {
+			dev->irq = irq;
+			printk("using probed IRQ %d.\n", dev->irq);
+		} else
+			printk("failed to detect IRQ(%d) --"
+			       " Please set IRQ by ifconfig.\n", irq);
+	}
+
+	request_region(PAR_DATA(dev), iosize, dev->name);
+
+	/* Fill in the generic fields of the device structure. */
+	ether_setup(dev);
+
+	/* Then, override parts of it */
+	dev->hard_start_xmit	= plip_tx_packet;
+	dev->open		= plip_open;
+	dev->stop		= plip_close;
+	dev->get_stats 		= plip_get_stats;
+	dev->set_config		= plip_config;
+	dev->do_ioctl		= plip_ioctl;
+	dev->flags	        = IFF_POINTOPOINT|IFF_NOARP;
+
+	/* Set the private structure */
+	dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL) {
+		printk(KERN_ERR "%s: out of memory\n", dev->name);
+		release_region(PAR_DATA(dev), iosize);
+		return -ENOMEM;
+	}
+	memset(dev->priv, 0, sizeof(struct net_local));
+	nl = (struct net_local *) dev->priv;
+
+	nl->orig_rebuild_header = dev->rebuild_header;
+	dev->rebuild_header 	= plip_rebuild_header;
+
+	/* Initialize constants */
+	nl->trigger	= PLIP_TRIGGER_WAIT;
+	nl->nibble	= PLIP_NIBBLE_WAIT;
+	nl->iostate	= RACK_UNKNOWN | TACK_UNKNOWN | TACK_HIGH;
+
+	/* Initialize task queue structures */
+	nl->immediate.next = &tq_last;
+	nl->immediate.sync = 0;
+	nl->immediate.routine = (void *)(void *)plip_bh;
+	nl->immediate.data = dev;
+
+	nl->deferred.next = &tq_last;
+	nl->deferred.sync = 0;
+	nl->deferred.routine = (void *)(void *)plip_kick_bh;
+	nl->deferred.data = dev;
+
+	return 0;
+}
+
+/* Bottom half handler for the delayed request.
+   This routine is kicked by do_timer().
+   Request `plip_bh' to be invoked. */
+static void
+plip_kick_bh(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+
+	if (nl->is_deferred) {
+		queue_task(&nl->immediate, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+}
+
+/* Forward declarations of internal routines */
+static int plip_none(struct device *, struct net_local *,
+		     struct plip_local *, struct plip_local *);
+static int plip_receive_packet(struct device *, struct net_local *,
+			       struct plip_local *, struct plip_local *);
+static int plip_send_packet(struct device *, struct net_local *,
+			    struct plip_local *, struct plip_local *);
+static int plip_connection_close(struct device *, struct net_local *,
+				 struct plip_local *, struct plip_local *);
+static int plip_error(struct device *, struct net_local *,
+		      struct plip_local *, struct plip_local *);
+static int plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+				 struct plip_local *snd,
+				 struct plip_local *rcv,
+				 int error);
+
+#define OK        0
+#define TIMEOUT   1
+#define ERROR     2
+
+typedef int (*plip_func)(struct device *dev, struct net_local *nl,
+			 struct plip_local *snd, struct plip_local *rcv);
+
+static plip_func connection_state_table[] =
+{
+	plip_none,
+	plip_receive_packet,
+	plip_send_packet,
+	plip_connection_close,
+	plip_error
+};
+
+/* Bottom half handler of PLIP. */
+static void
+plip_bh(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct plip_local *snd = &nl->snd_data;
+	struct plip_local *rcv = &nl->rcv_data;
+	plip_func f;
+	int r;
+
+	nl->is_deferred = 0;
+	f = connection_state_table[nl->connection];
+	if ((r = (*f)(dev, nl, snd, rcv)) != OK
+	    && (r = plip_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
+		nl->is_deferred = 1;
+		queue_task(&nl->deferred, &tq_timer);
+	}
+}
+
+static int
+plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+		      struct plip_local *snd, struct plip_local *rcv,
+		      int error)
+{
+	unsigned char c0;
+
+	cli();
+	if (nl->connection == PLIP_CN_SEND) {
+
+		if (error != ERROR) { /* Timeout */
+			nl->timeout_count++;
+			if (snd->state == PLIP_PK_TRIGGER) {
+				if (IOSTATE(nl) & TACK_UNKNOWN)
+					IOSTATE(nl) ^= TACK_HIGH;
+			}
+			if ((snd->state == PLIP_PK_TRIGGER
+			     && nl->timeout_count <= 10)
+			    || nl->timeout_count <= 3) {
+				sti();
+				/* Try again later */
+				return TIMEOUT;
+			}
+			c0 = inb(PAR_STATUS(dev));
+			printk("%s: transmit timeout(%d,%02x)\n",
+			       dev->name, snd->state, c0);
+		}
+		nl->enet_stats.tx_errors++;
+		nl->enet_stats.tx_aborted_errors++;
+	} else if (nl->connection == PLIP_CN_RECEIVE) {
+		if (rcv->state == PLIP_PK_TRIGGER) {
+			/* Transmission was interrupted. */
+			sti();
+			return OK;
+		}
+		if (error != ERROR) { /* Timeout */
+			if (++nl->timeout_count <= 3) {
+				sti();
+				/* Try again later */
+				return TIMEOUT;
+			}
+			c0 = inb(PAR_STATUS(dev));
+			printk("%s: receive timeout(%d,%02x)\n",
+			       dev->name, rcv->state, c0);
+		}
+		nl->enet_stats.rx_dropped++;
+	}
+	rcv->state = PLIP_PK_DONE;
+	if (rcv->skb) {
+		rcv->skb->free = 1;
+		kfree_skb(rcv->skb, FREE_READ);
+		rcv->skb = NULL;
+	}
+	snd->state = PLIP_PK_DONE;
+	if (snd->skb) {
+		dev_kfree_skb(snd->skb, FREE_WRITE);
+		snd->skb = NULL;
+	}
+	disable_irq(dev->irq);
+	outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+	dev->tbusy = 1;
+	nl->connection = PLIP_CN_ERROR;
+	if (IOSTATE(nl) & TACK_HIGH)
+		outb(0x00, PAR_DATA(dev));
+	else
+		outb(0x08, PAR_DATA(dev));
+	sti();
+
+	return TIMEOUT;
+}
+
+static int
+plip_none(struct device *dev, struct net_local *nl,
+	  struct plip_local *snd, struct plip_local *rcv)
+{
+	return OK;
+}
+
+/* PLIP_RECEIVE --- receive a byte(two nibbles)
+   Returns OK on success, TIMEOUT on timeout */
+inline static int
+plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
+	     enum plip_nibble_state *ns_p, unsigned char *data_p)
+{
+	unsigned char c0, c1;
+	unsigned int cx;
+
+	switch (*ns_p) {
+	case PLIP_NB_BEGIN:
+		cx = nibble_timeout;
+		while (1) {
+			c0 = inb(status_addr);
+			udelay(PLIP_DELAY_UNIT);
+			if ((c0 & 0x80) == 0) {
+				c1 = inb(status_addr);
+				if (c0 == c1)
+					break;
+			}
+			if (--cx == 0)
+				return TIMEOUT;
+		}
+		*data_p = (c0 >> 3) & 0x0f;
+		outb(0x10, --status_addr); /* send ACK */
+		status_addr++;
+		*ns_p = PLIP_NB_1;
+
+	case PLIP_NB_1:
+		cx = nibble_timeout;
+		while (1) {
+			c0 = inb(status_addr);
+			udelay(PLIP_DELAY_UNIT);
+			if (c0 & 0x80) {
+				c1 = inb(status_addr);
+				if (c0 == c1)
+					break;
+			}
+			if (--cx == 0)
+				return TIMEOUT;
+		}
+		*data_p |= (c0 << 1) & 0xf0;
+		outb(0x00, --status_addr); /* send ACK */
+		status_addr++;
+		*ns_p = PLIP_NB_BEGIN;
+	case PLIP_NB_2:
+		break;
+	}
+	return OK;
+}
+
+/* PLIP_RECEIVE_PACKET --- receive a packet */
+static int
+plip_receive_packet(struct device *dev, struct net_local *nl,
+		    struct plip_local *snd, struct plip_local *rcv)
+{
+	unsigned short status_addr = PAR_STATUS(dev);
+	unsigned short nibble_timeout = nl->nibble;
+	unsigned char *lbuf;
+
+	switch (rcv->state) {
+	case PLIP_PK_TRIGGER:
+		disable_irq(dev->irq);
+		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+		dev->interrupt = 0;
+		if (IOSTATE(nl) & TACK_HIGH)
+			outb(0x01, PAR_DATA(dev)); /* send ACK */
+		else
+			outb(0x09, PAR_DATA(dev)); /* send ACK */
+		if (net_debug > 2)
+			printk("%s: receive start\n", dev->name);
+		rcv->state = PLIP_PK_LENGTH_LSB;
+		rcv->nibble = PLIP_NB_BEGIN;
+
+	case PLIP_PK_LENGTH_LSB:
+		if (snd->state != PLIP_PK_DONE) {
+			if (plip_receive(nl->trigger, status_addr,
+					 &rcv->nibble, &rcv->length.b.lsb)) {
+				/* collision, here dev->tbusy == 1 */
+				rcv->state = PLIP_PK_DONE;
+				nl->is_deferred = 1;
+				nl->connection = PLIP_CN_SEND;
+				queue_task(&nl->deferred, &tq_timer);
+				outb(PAR_INTR_ON, PAR_CONTROL(dev));
+				RESET_INTERRUPT;
+				enable_irq(dev->irq);
+				return OK;
+			}
+		} else {
+			if (plip_receive(nibble_timeout, status_addr,
+					 &rcv->nibble, &rcv->length.b.lsb))
+				return TIMEOUT;
+		}
+		rcv->state = PLIP_PK_LENGTH_MSB;
+
+	case PLIP_PK_LENGTH_MSB:
+		if (plip_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &rcv->length.b.msb))
+			return TIMEOUT;
+		if (rcv->length.h > dev->mtu + dev->hard_header_len
+		    || rcv->length.h < 8) {
+			printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
+			return ERROR;
+		}
+		/* Malloc up new buffer. */
+		rcv->skb = dev_alloc_skb(rcv->length.h);
+		if (rcv->skb == NULL) {
+			printk("%s: Memory squeeze.\n", dev->name);
+			return ERROR;
+		}
+		skb_reserve (rcv->skb, -2);
+		skb_put(rcv->skb,rcv->length.h);
+		rcv->skb->dev = dev;
+		rcv->state = PLIP_PK_DATA;
+		rcv->byte = 0;
+		rcv->checksum = 0;
+
+	case PLIP_PK_DATA:
+		lbuf = rcv->skb->data;
+		do
+			if (plip_receive(nibble_timeout, status_addr, 
+					 &rcv->nibble, &lbuf[rcv->byte]))
+				return TIMEOUT;
+		while (++rcv->byte < rcv->length.h);
+		do
+			rcv->checksum += lbuf[--rcv->byte];
+		while (rcv->byte);
+		rcv->state = PLIP_PK_CHECKSUM;
+
+	case PLIP_PK_CHECKSUM:
+		if (plip_receive(nibble_timeout, status_addr,
+				 &rcv->nibble, &rcv->data))
+			return TIMEOUT;
+		if (rcv->data != rcv->checksum) {
+			nl->enet_stats.rx_crc_errors++;
+			if (net_debug)
+				printk("%s: checksum error\n", dev->name);
+			return ERROR;
+		}
+		rcv->state = PLIP_PK_DONE;
+
+	case PLIP_PK_DONE:
+		/* Inform the upper layer for the arrival of a packet. */
+		rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
+		netif_rx(rcv->skb);
+		nl->enet_stats.rx_packets++;
+		rcv->skb = NULL;
+		if (net_debug > 2)
+			printk("%s: receive end\n", dev->name);
+
+		/* Close the connection. */
+		if (IOSTATE(nl) & TACK_HIGH)
+			outb (0x00, PAR_DATA(dev));
+		else
+			outb (0x08, PAR_DATA(dev));
+		cli();
+		if (snd->state != PLIP_PK_DONE) {
+			nl->connection = PLIP_CN_SEND;
+			sti();
+			queue_task(&nl->immediate, &tq_immediate);
+			outb(PAR_INTR_ON, PAR_CONTROL(dev));
+			RESET_INTERRUPT;
+			enable_irq(dev->irq);
+			return OK;
+		} else {
+			nl->connection = PLIP_CN_NONE;
+			sti();
+			outb(PAR_INTR_ON, PAR_CONTROL(dev));
+			RESET_INTERRUPT;
+			enable_irq(dev->irq);
+			return OK;
+		}
+	}
+	return OK;
+}
+
+/* PLIP_SEND --- send a byte (two nibbles) 
+   Returns OK on success, TIMEOUT when timeout    */
+inline static int
+plip_send(unsigned short nibble_timeout, unsigned short data_addr,
+	  enum plip_nibble_state *ns_p, unsigned char data)
+{
+	unsigned char c0;
+	unsigned int cx;
+
+	switch (*ns_p) {
+	case PLIP_NB_BEGIN:
+		outb((data & 0x0f), data_addr);
+		*ns_p = PLIP_NB_1;
+
+	case PLIP_NB_1:
+		outb(0x10 | (data & 0x0f), data_addr);
+		cx = nibble_timeout;
+		data_addr++;
+		while (1) {
+			c0 = inb(data_addr);
+			if ((c0 & 0x80) == 0) 
+				break;
+			if (--cx == 0)
+				return TIMEOUT;
+			udelay(PLIP_DELAY_UNIT);
+		}
+		outb(0x10 | (data >> 4), --data_addr);
+		*ns_p = PLIP_NB_2;
+
+	case PLIP_NB_2:
+		outb((data >> 4), data_addr);
+		data_addr++;
+		cx = nibble_timeout;
+		while (1) {
+			c0 = inb(data_addr);
+			if (c0 & 0x80)
+				break;
+			if (--cx == 0)
+				return TIMEOUT;
+			udelay(PLIP_DELAY_UNIT);
+		}
+		data_addr--;
+		*ns_p = PLIP_NB_BEGIN;
+		return OK;
+	}
+	return OK;
+}
+
+/* PLIP_SEND_PACKET --- send a packet */
+static int
+plip_send_packet(struct device *dev, struct net_local *nl,
+		 struct plip_local *snd, struct plip_local *rcv)
+{
+	unsigned short data_addr = PAR_DATA(dev);
+	unsigned short nibble_timeout = nl->nibble;
+	unsigned char *lbuf;
+	unsigned char c0;
+	unsigned int cx;
+
+	if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
+		printk("%s: send skb lost\n", dev->name);
+		snd->state = PLIP_PK_DONE;
+		snd->skb = NULL;
+		return ERROR;
+	}
+
+	switch (snd->state) {
+	case PLIP_PK_TRIGGER:
+		switch (IOSTATE(nl) & RACK_STATE) {
+			case RACK_UNKNOWN: /* we don't know the active edge of ACK */
+				if ((inb(PAR_STATUS(dev)) & 0xb8) != 0x80)
+					return TIMEOUT;
+				break;
+
+			case RACK_ACTIVEHIGH: /* PC-style intr when ACK 0 -> 1 */
+				if ((inb(PAR_STATUS(dev)) & 0xf8) != 0x80)
+					return TIMEOUT;
+				break;
+
+			case RACK_ACTIVELOW: /* ARM-style intr when ACK 1 -> 0 */
+				if ((inb(PAR_STATUS(dev)) & 0xf8) != 0xc0)
+					return TIMEOUT;
+				break;
+		}
+
+		/* Trigger remote rx interrupt. */
+		if (IOSTATE(nl) & TACK_HIGH)
+			outb(0x08, data_addr);
+		else
+			outb(0x00, data_addr);
+		cx = nl->trigger;
+		while (1) {
+			udelay(PLIP_DELAY_UNIT);
+			cli();
+			if (nl->connection == PLIP_CN_RECEIVE) {
+				sti();
+				/* interrupted */
+				nl->enet_stats.collisions++;
+				if (net_debug > 1)
+					printk("%s: collision.\n", dev->name);
+				return OK;
+			}
+			c0 = inb(PAR_STATUS(dev));
+			if (c0 & 0x08) {
+				disable_irq(dev->irq);
+				outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+				if (net_debug > 2)
+					printk("%s: send start\n", dev->name);
+				snd->state = PLIP_PK_LENGTH_LSB;
+				snd->nibble = PLIP_NB_BEGIN;
+				nl->timeout_count = 0;
+				sti();
+				break;
+			}
+			sti();
+			if (--cx == 0) {
+				if (IOSTATE(nl) & TACK_HIGH)
+					outb(0x00, data_addr);
+				else
+					outb(0x08, data_addr);
+				return TIMEOUT;
+			}
+		}
+		if (IOSTATE(nl) & TACK_UNKNOWN) {
+			if (net_debug > 1)
+				printk ("%s: remote ACK active %s\n", dev->name, IOSTATE(nl) & TACK_HIGH ? "high" : "low");
+			IOSTATE(nl) &= ~TACK_UNKNOWN;
+		}
+
+	case PLIP_PK_LENGTH_LSB:
+		if (plip_send(nibble_timeout, data_addr,
+			      &snd->nibble, snd->length.b.lsb))
+			return TIMEOUT;
+		snd->state = PLIP_PK_LENGTH_MSB;
+
+	case PLIP_PK_LENGTH_MSB:
+		if (plip_send(nibble_timeout, data_addr,
+			      &snd->nibble, snd->length.b.msb))
+			return TIMEOUT;
+		snd->state = PLIP_PK_DATA;
+		snd->byte = 0;
+		snd->checksum = 0;
+
+	case PLIP_PK_DATA:
+		do
+			if (plip_send(nibble_timeout, data_addr,
+				      &snd->nibble, lbuf[snd->byte]))
+				return TIMEOUT;
+		while (++snd->byte < snd->length.h);
+		do
+			snd->checksum += lbuf[--snd->byte];
+		while (snd->byte);
+		snd->state = PLIP_PK_CHECKSUM;
+
+	case PLIP_PK_CHECKSUM:
+		if (plip_send(nibble_timeout, data_addr,
+			      &snd->nibble, snd->checksum))
+			return TIMEOUT;
+
+		dev_kfree_skb(snd->skb, FREE_WRITE);
+		nl->enet_stats.tx_packets++;
+		snd->state = PLIP_PK_DONE;
+
+	case PLIP_PK_DONE:
+		/* Close the connection */
+		if (IOSTATE(nl) & TACK_HIGH)
+			outb (0x00, data_addr);
+		else
+			outb (0x08, data_addr);
+		snd->skb = NULL;
+		if (net_debug > 2)
+			printk("%s: send end\n", dev->name);
+		nl->connection = PLIP_CN_CLOSING;
+		nl->is_deferred = 1;
+		queue_task(&nl->deferred, &tq_timer);
+		outb(PAR_INTR_ON, PAR_CONTROL(dev));
+		RESET_INTERRUPT;
+		enable_irq(dev->irq);
+		return OK;
+	}
+	return OK;
+}
+
+static int
+plip_connection_close(struct device *dev, struct net_local *nl,
+		      struct plip_local *snd, struct plip_local *rcv)
+{
+	cli();
+	if (nl->connection == PLIP_CN_CLOSING) {
+		nl->connection = PLIP_CN_NONE;
+		dev->tbusy = 0;
+		mark_bh(NET_BH);
+	}
+	sti();
+	return OK;
+}
+
+/* PLIP_ERROR --- wait till other end settled */
+static int
+plip_error(struct device *dev, struct net_local *nl,
+	   struct plip_local *snd, struct plip_local *rcv)
+{
+	unsigned char status;
+
+	status = inb(PAR_STATUS(dev));
+	switch (IOSTATE(nl) & RACK_STATE) {
+		case RACK_UNKNOWN:	/* we can't reset the interface */
+			nl->is_deferred = 1;
+			queue_task(&nl->deferred, &tq_timer);
+			return OK;
+
+		case RACK_ACTIVEHIGH:	/* PC-style */
+			if ((status & 0xf8) != 0x80) {
+				nl->is_deferred = 1;
+				queue_task(&nl->deferred, &tq_timer);
+				return OK;
+			}
+			break;
+		case RACK_ACTIVELOW:	/* ARM-style */
+			if ((status & 0xf8) != 0xc0) {
+				nl->is_deferred = 1;
+				queue_task(&nl->deferred, &tq_timer);
+				return OK;
+			}
+			break;
+	}
+	if (net_debug > 2)
+		printk("%s: reset interface.\n", dev->name);
+	nl->connection = PLIP_CN_NONE;
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	outb(PAR_INTR_ON, PAR_CONTROL(dev));
+	RESET_INTERRUPT;
+	enable_irq(dev->irq);
+	mark_bh(NET_BH);
+	return OK;
+}
+
+/* Handle the parallel port interrupts. */
+static void
+plip_interrupt(int irq, struct pt_regs * regs)
+{
+	struct device *dev = (struct device *) irq2dev_map[irq];
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct plip_local *rcv = &nl->rcv_data;
+	unsigned char c0;
+
+	RESET_INTERRUPT;
+
+	if (dev == NULL) {
+		printk ("plip_interrupt: irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	if (dev->interrupt)
+		return;
+
+	c0 = inb(PAR_STATUS(dev));
+	switch (IOSTATE(nl) & RACK_STATE) {
+		case RACK_UNKNOWN:	/* don't know */
+			/*
+			 * Here, we rely on the first interrupt being correct
+			 * and not spurious...  This is only used if we don't know
+			 * which edge that the ACK interrupt triggers on.
+			 */
+			if ((c0 & 0xf8) == 0xc0) {
+				IOSTATE(nl) = (IOSTATE(nl) & ~RACK_STATE) | RACK_ACTIVEHIGH;
+				printk ("%s: ACK active high\n", dev->name);
+			} else
+			if ((c0 & 0xf8) == 0x80) {
+				IOSTATE(nl) = (IOSTATE(nl) & ~RACK_STATE) | RACK_ACTIVELOW;
+				printk ("%s: ACK active low\n", dev->name);
+			} else {
+				if (net_debug > 1)
+					printk ("%s: spurious interrupt\n", dev->name);
+				return;
+			}
+			break;
+
+		case RACK_ACTIVEHIGH:	/* ACK intr when ACK 0 -> 1 */
+			if ((c0 & 0xf8) != 0xc0) {
+				if (net_debug > 1)
+					printk ("%s: spurious interrupt\n", dev->name);
+				return;
+			}
+			break;
+
+		case RACK_ACTIVELOW:	/* ACK intr when ACK 1 -> 0 */
+			if ((c0 & 0xf8) != 0x80) {
+				if (net_debug > 1)
+					printk ("%s: spurious interrupt\n", dev->name);
+				return;
+			}
+			break;
+	}
+	dev->interrupt = 1;
+	if (net_debug > 3)
+		printk("%s: interrupt.\n", dev->name);
+
+	cli();
+	switch (nl->connection) {
+	case PLIP_CN_CLOSING:
+		dev->tbusy = 0;
+	case PLIP_CN_NONE:
+	case PLIP_CN_SEND:
+		dev->last_rx = jiffies;
+		rcv->state = PLIP_PK_TRIGGER;
+		nl->connection = PLIP_CN_RECEIVE;
+		nl->timeout_count = 0;
+		queue_task(&nl->immediate, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+		sti();
+		break;
+
+	case PLIP_CN_RECEIVE:
+		sti();
+		printk("%s: receive interrupt when receiving packet\n", dev->name);
+		break;
+
+	case PLIP_CN_ERROR:
+		sti();
+		printk("%s: receive interrupt in error state\n", dev->name);
+		break;
+	}
+}
+
+/* We don't need to send arp, for plip is point-to-point. */
+static int
+plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+		    struct sk_buff *skb)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct ethhdr *eth = (struct ethhdr *)buff;
+	int i;
+
+	if ((dev->flags & IFF_NOARP)==0)
+		return nl->orig_rebuild_header(buff, dev, dst, skb);
+
+	if (eth->h_proto != htons(ETH_P_IP)) {
+		printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
+		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+		return 0;
+	}
+
+	for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+		eth->h_dest[i] = 0xfc;
+	memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
+	return 0;
+}
+
+static int
+plip_tx_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct plip_local *snd = &nl->snd_data;
+
+	if (dev->tbusy)
+		return 1;
+
+	/* If some higher layer thinks we've missed an tx-done interrupt
+	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+	   itself. */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	if (set_bit(0, (void*)&dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+		return 1;
+	}
+
+	if (skb->len > dev->mtu + dev->hard_header_len) {
+		printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
+		dev->tbusy = 0;
+		return 0;
+	}
+
+	if (net_debug > 2)
+		printk("%s: send request\n", dev->name);
+
+	cli();
+	dev->trans_start = jiffies;
+	snd->skb = skb;
+	snd->length.h = skb->len;
+	snd->state = PLIP_PK_TRIGGER;
+	if (nl->connection == PLIP_CN_NONE) {
+		nl->connection = PLIP_CN_SEND;
+		nl->timeout_count = 0;
+	}
+	queue_task(&nl->immediate, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	sti();
+
+	return 0;
+}
+
+/* Open/initialize the board.  This is called (in the current kernel)
+   sometime after booting when the 'ifconfig' program is run.
+
+   This routine gets exclusive access to the parallel port by allocating
+   its IRQ line.
+ */
+static int
+plip_open(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	int i;
+
+	if (dev->irq == NO_IRQ) {
+		printk("%s: IRQ is not set.  Please set it by ifconfig.\n", dev->name);
+		return -EAGAIN;
+	}
+	cli();
+	if (request_irq(dev->irq , plip_interrupt, 0, dev->name) != 0) {
+		sti();
+		printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
+		return -EAGAIN;
+	}
+	irq2dev_map[dev->irq] = dev;
+	sti();
+
+	/* Clear the data port. */
+	if (IOSTATE(nl) & TACK_HIGH)
+		outb (0x00, PAR_DATA(dev));
+	else
+		outb (0x08, PAR_DATA(dev));
+
+	/* Enable rx interrupt. */
+	cli ();
+	outb(PAR_INTR_ON, PAR_CONTROL(dev));
+	RESET_INTERRUPT;
+	sti ();
+
+	/* Initialize the state machine. */
+	nl->rcv_data.state = nl->snd_data.state = PLIP_PK_DONE;
+	nl->rcv_data.skb = nl->snd_data.skb = NULL;
+	nl->connection = PLIP_CN_NONE;
+	nl->is_deferred = 0;
+	nl->iostate = RACK_ACTIVELOW | TACK_UNKNOWN | TACK_HIGH;
+
+	/* Fill in the MAC-level header. */
+	for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+		dev->dev_addr[i] = 0xfc;
+	memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(unsigned long));
+
+	dev->interrupt = 0;
+	dev->start = 1;
+	dev->tbusy = 0;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* The inverse routine to plip_open (). */
+static int
+plip_close(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct plip_local *snd = &nl->snd_data;
+	struct plip_local *rcv = &nl->rcv_data;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+	cli();
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = NULL;
+	nl->is_deferred = 0;
+	nl->connection = PLIP_CN_NONE;
+	sti();
+	if (IOSTATE(nl) & TACK_HIGH)
+		outb(0x00, PAR_DATA(dev));
+	else
+		outb(0x08, PAR_DATA(dev));
+
+	snd->state = PLIP_PK_DONE;
+	if (snd->skb) {
+		dev_kfree_skb(snd->skb, FREE_WRITE);
+		snd->skb = NULL;
+	}
+	rcv->state = PLIP_PK_DONE;
+	if (rcv->skb) {
+		rcv->skb->free = 1;
+		kfree_skb(rcv->skb, FREE_READ);
+		rcv->skb = NULL;
+	}
+
+	/* Reset. */
+	outb(0x00, PAR_CONTROL(dev));
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct enet_statistics *
+plip_get_stats(struct device *dev)
+{
+	struct net_local *nl = (struct net_local *)dev->priv;
+	struct enet_statistics *r = &nl->enet_stats;
+
+	return r;
+}
+
+static int
+plip_config(struct device *dev, struct ifmap *map)
+{
+	if (dev->flags & IFF_UP)
+		return -EBUSY;
+
+	if (map->base_addr != (unsigned long)-1
+	    && map->base_addr != dev->base_addr)
+		printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
+
+	if (map->irq != (unsigned char)-1)
+		dev->irq = map->irq;
+	return 0;
+}
+
+static int
+plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	struct net_local *nl = (struct net_local *) dev->priv;
+	struct plipconf *pc = (struct plipconf *) &rq->ifr_data;
+	
+	switch(pc->pcmd) {
+	case PLIP_GET_TIMEOUT:
+		pc->trigger = nl->trigger;
+		pc->nibble  = nl->nibble;
+		break;
+	case PLIP_SET_TIMEOUT:
+		nl->trigger = pc->trigger;
+		nl->nibble  = pc->nibble;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+int io[] = {0, 0, 0};
+int irq[] = {NO_IRQ, NO_IRQ, NO_IRQ};
+
+static struct device dev_plip[] = {
+	{
+		"plip0",
+		0, 0, 0, 0,		/* memory */
+		0x3BC, NO_IRQ,		/* base, irq */
+		0, 0, 0, NULL, plip_init 
+	},
+	{
+		"plip1",
+		0, 0, 0, 0,		/* memory */
+		0x378, NO_IRQ,		/* base, irq */
+		0, 0, 0, NULL, plip_init 
+	},
+	{
+		"plip2",
+		0, 0, 0, 0,		/* memory */
+		0x278, 0,		/* base, irq */
+		0, 0, 0, NULL, plip_init 
+	}
+};
+
+int
+init_module(void)
+{
+	int no_parameters=1;
+	int devices=0;
+	int i;
+
+	/* When user feeds parameters, use them */
+	for (i=0; i < 3; i++) {
+		int specified=0;
+
+		if (io[i] != 0) {
+			dev_plip[i].base_addr = io[i];
+			specified++;
+		}
+		if (irq[i] != NO_IRQ) {
+			dev_plip[i].irq = irq[i];
+			specified++;
+		}
+		if (specified) {
+			if (register_netdev(&dev_plip[i]) != 0) {
+				printk(KERN_INFO "plip%d: Not found\n", i);
+				return -EIO;
+			}
+			no_parameters = 0;
+		}
+	}
+	if (!no_parameters)
+		return 0;
+
+	/* No parameters.  Default action is probing all interfaces. */
+	for (i=0; i < 3; i++) { 
+		if (register_netdev(&dev_plip[i]) == 0)
+			devices++;
+	}
+	if (devices == 0) {
+		printk(KERN_INFO "plip: no interfaces found\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	int i;
+
+	for (i=0; i < 3; i++) {
+		if (dev_plip[i].priv) {
+			unregister_netdev(&dev_plip[i]);
+			release_region(PAR_DATA(&dev_plip[i]), (PAR_DATA(&dev_plip[i]) == 0x3bc)? 3 : 8);
+			kfree_s(dev_plip[i].priv, sizeof(struct net_local));
+			dev_plip[i].priv = NULL;
+		}
+	}
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c plip.c"
+ * End:
+ */
diff -urNwbB linux/arch/arm/drivers/net/ppp.c linux.arm/arch/arm/drivers/net/ppp.c
--- linux/arch/arm/drivers/net/ppp.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ppp.c	Sat Feb 24 09:36:53 1996
@@ -0,0 +1,3634 @@
+/*  PPP for Linux
+ *
+ *  Michael Callahan <callahan@maths.ox.ac.uk>
+ *  Al Longyear <longyear@netcom.com>
+ *
+ *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
+ *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
+ *
+ *  ==FILEVERSION 7==
+ *
+ *  NOTE TO MAINTAINERS:
+ *     If you modify this file at all, increment the number above.
+ *     ppp.c is shipped with a PPP distribution as well as with the kernel;
+ *     if everyone increases the FILEVERSION number above, then scripts
+ *     can do the right thing when deciding whether to install a new ppp.c
+ *     file.  Don't change the format of that line otherwise, so the
+ *     installation script can recognize it.
+ */
+
+/*
+   Sources:
+
+   slip.c
+
+   RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of
+   Multi-protocol Datagrams over Point-to-Point Links
+
+   RFC1332: IPCP
+
+   ppp-2.0
+
+   Flags for this module (any combination is acceptable for testing.):
+
+   OPTIMIZE_FLAG_TIME - Number of jiffies to force sending of leading flag
+			character. This is normally set to ((HZ * 3) / 2).
+			This is 1.5 seconds. If zero then the leading
+			flag is always sent.
+
+   CHECK_CHARACTERS   - Enable the checking on all received characters for
+			8 data bits, no parity. This adds a small amount of
+			processing for each received character.
+			
+   NEW_SKBUFF	      - Use NET3.020 sk_buff's
+*/
+
+/* #define NEW_SKBUFF		1 */
+#define OPTIMIZE_FLAG_TIME	((HZ * 3)/2)
+
+#define CHECK_CHARACTERS	1
+#define PPP_COMPRESS		1
+#define USE_SKB_PROTOCOL 1  /* Set by the installation program! */
+
+#ifdef  NEW_SKBUFF
+#undef  USE_SKB_PROTOCOL
+#define USE_SKB_PROTOCOL 2
+#endif
+
+#ifndef PPP_MAX_DEV
+#define PPP_MAX_DEV	256
+#endif
+
+/* $Id: ppp.c,v 1.5 1995/06/12 11:36:53 paulus Exp $
+ * Added dynamic allocation of channels to eliminate
+ *   compiled-in limits on the number of channels.
+ *
+ * Dynamic channel allocation code Copyright 1995 Caldera, Inc.,
+ *   released under the GNU General Public License Version 2.
+ */
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+
+#include <endian.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/sched.h>	/* to get the struct task_struct */
+#include <linux/string.h>	/* used in new tty drivers */
+#include <linux/signal.h>	/* used in new tty drivers */
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <net/if.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+
+#ifdef NEW_SKBUFF
+#include <linux/netprotocol.h>
+#else
+typedef struct sk_buff	     sk_buff;
+#define skb_data(skb)	     ((unsigned char *) (skb)->data)
+#endif
+
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <linux/if_arp.h>
+#include "slhc.h"
+#include <linux/ppp_defs.h>
+#include <linux/socket.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppvar.h>
+
+#undef   PACKETPTR
+#define  PACKETPTR 1
+#include <linux/ppp-comp.h>
+#undef   PACKETPTR
+
+#define bsd_decompress	(*ppp->sc_rcomp->decompress)
+#define bsd_compress	(*ppp->sc_xcomp->compress)
+
+#ifndef PPP_IPX
+#define PPP_IPX 0x2b  /* IPX protocol over PPP */
+#endif
+
+#ifndef PPP_LQR
+#define PPP_LQR 0xc025  /* Link Quality Reporting Protocol */
+#endif
+
+static int ppp_register_compressor (struct compressor *cp);
+static void ppp_unregister_compressor (struct compressor *cp);
+
+/*
+ * Local functions
+ */
+
+static struct compressor *find_compressor (int type);
+static void ppp_init_ctrl_blk (register struct ppp *);
+static void ppp_kick_tty (struct ppp *, struct ppp_buffer *bfr);
+static int ppp_doframe (struct ppp *);
+static struct ppp *ppp_alloc (void);
+static void ppp_print_buffer (const u_char *, const u_char *, int);
+extern inline void ppp_stuff_char (struct ppp *ppp,
+				   register struct ppp_buffer *buf,
+				   register u_char chr);
+extern inline int lock_buffer (register struct ppp_buffer *buf);
+
+static int rcv_proto_ip         (struct ppp *, u_short, u_char *, int);
+static int rcv_proto_ipx        (struct ppp *, u_short, u_char *, int);
+static int rcv_proto_vjc_comp   (struct ppp *, u_short, u_char *, int);
+static int rcv_proto_vjc_uncomp (struct ppp *, u_short, u_char *, int);
+static int rcv_proto_unknown    (struct ppp *, u_short, u_char *, int);
+static int rcv_proto_lqr        (struct ppp *, u_short, u_char *, int);
+static void ppp_doframe_lower   (struct ppp *, u_char *, int);
+static int ppp_doframe          (struct ppp *);
+
+extern int  ppp_bsd_compressor_init(void);
+static void ppp_proto_ccp (struct ppp *ppp, u_char *dp, int len, int rcvd);
+static int  rcv_proto_ccp (struct ppp *, u_short, u_char *, int);
+
+#define ins_char(pbuf,c) (buf_base(pbuf) [(pbuf)->count++] = (u_char)(c))
+
+#ifndef OPTIMIZE_FLAG_TIME
+#define OPTIMIZE_FLAG_TIME	0
+#endif
+
+#ifndef PPP_MAX_DEV
+#define PPP_MAX_DEV 256
+#endif
+
+/*
+ * Parameters which may be changed via insmod.
+ */
+
+static int  flag_time = OPTIMIZE_FLAG_TIME;
+static int  max_dev   = PPP_MAX_DEV;
+
+/*
+ * The "main" procedure to the ppp device
+ */
+
+int ppp_init (struct device *);
+
+/*
+ * Network device driver callback routines
+ */
+
+static int ppp_dev_open (struct device *);
+static int ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd);
+static int ppp_dev_close (struct device *);
+static int ppp_dev_xmit (sk_buff *, struct device *);
+static struct enet_statistics *ppp_dev_stats (struct device *);
+
+#if USE_SKB_PROTOCOL == 0  /* The 1.2.x kernel is here */
+#define dev_alloc_skb(count)        alloc_skb(count, GFP_ATOMIC)
+#define skb_put(skb,count)          skb_data(skb)
+#define get_long_user(addr)	    get_user_long((void *) addr)
+#define get_int_user(addr)	    ((int) get_user_long((void *) addr))
+#define put_byte_user(val,addr)	    put_fs_byte(val,((u_char *) (addr)))
+#define put_long_user(val,addr)	    put_fs_long((val),((void *) (addr)))
+
+static unsigned short ppp_dev_type (sk_buff *, struct device *);
+static int ppp_dev_header (unsigned char *buff, struct device *dev,
+			   unsigned short type, void *daddr, void *saddr,
+			   unsigned len, struct sk_buff *skb);
+
+#else /* The 1.3.x kernel is here */
+#define get_long_user(addr)	    get_user(((int *) addr))
+#define get_int_user(addr)	    ((int) get_user(((int *) addr)))
+#define put_byte_user(val,addr)	    put_user((val),((u_char *) (addr)))
+#define put_long_user(val,addr)	    put_user((val),((int *) (addr)))
+
+static int ppp_dev_header (sk_buff *, struct device *, unsigned short,
+			   void *, void *, unsigned);
+#endif
+
+#ifdef NEW_SKBUFF
+static int ppp_dev_input (struct protocol *self, struct protocol *lower,
+			  sk_buff *skb, void *saddr, void *daddr);
+static int ppp_dev_output (struct protocol *self, sk_buff *skb, int type,
+			   int subid, void *saddr, void *daddr, void *opt);
+static int ppp_dev_getkey(int protocol, int subid, unsigned char *key);
+#else
+static int ppp_dev_rebuild (void *, struct device *, unsigned long,
+			    sk_buff *);
+#endif
+
+/*
+ * TTY callbacks
+ */
+
+static int ppp_tty_read (struct tty_struct *, struct file *, u_char *,
+			 unsigned int);
+static int ppp_tty_write (struct tty_struct *, struct file *, const u_char *,
+			  unsigned int);
+static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
+			  unsigned long);
+static int ppp_tty_select (struct tty_struct *tty, struct inode *inode,
+		      struct file *filp, int sel_type, select_table * wait);
+static int ppp_tty_open (struct tty_struct *);
+static void ppp_tty_close (struct tty_struct *);
+static int ppp_tty_room (struct tty_struct *tty);
+static void ppp_tty_receive (struct tty_struct *tty, const u_char * cp,
+			     char *fp, int count);
+static void ppp_tty_wakeup (struct tty_struct *tty);
+
+#define CHECK_PPP(a)  if (!ppp->inuse) { printk (ppp_warning, __LINE__); return a;}
+#define CHECK_PPP_VOID()  if (!ppp->inuse) { printk (ppp_warning, __LINE__); return;}
+
+#define in_xmap(ppp,c)	(ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
+#define in_rmap(ppp,c)	((((unsigned int) (u_char) (c)) < 0x20) && \
+			ppp->recv_async_map & (1 << (c)))
+
+#define bset(p,b)	((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
+
+#define tty2ppp(tty)	((struct ppp *) (tty->disc_data))
+#define dev2ppp(dev)	((struct ppp *) (dev->priv))
+#define ppp2tty(ppp)	((struct tty_struct *) ppp->tty)
+#define ppp2dev(ppp)	((struct device *) ppp->dev)
+
+struct ppp_hdr {
+	unsigned char address;
+	unsigned char control;
+	unsigned char protocol[2];
+};
+
+#define PPP_HARD_HDR_LEN	(sizeof (struct ppp_hdr))
+
+typedef struct  ppp_ctrl {
+	struct ppp_ctrl *next;		/* Next structure in the list	*/
+	char            name [8];	/* Name of the device		*/
+	struct ppp      ppp;		/* PPP control table		*/
+	struct device   dev;		/* Device information table	*/
+} ppp_ctrl_t;
+
+static ppp_ctrl_t *ppp_list = NULL;
+
+#define ctl2ppp(ctl) (struct ppp *)    &ctl->ppp
+#define ctl2dev(ctl) (struct device *) &ctl->dev
+#undef  PPP_NRUNIT
+
+/* Buffer types */
+#define BUFFER_TYPE_DEV_RD	0  /* ppp read buffer       */
+#define BUFFER_TYPE_TTY_WR	1  /* tty write buffer      */
+#define BUFFER_TYPE_DEV_WR	2  /* ppp write buffer      */
+#define BUFFER_TYPE_TTY_RD	3  /* tty read buffer       */
+#define BUFFER_TYPE_VJ		4  /* vj compression buffer */
+
+/* Define this string only once for all macro envocations */
+static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
+
+static char szVersion[]         = PPP_VERSION;
+ 
+#ifdef NEW_SKBUFF
+static struct protocol proto_ppp;
+#endif
+
+/*
+ * Information for the protocol decoder
+ */
+
+typedef int (*pfn_proto)  (struct ppp *, u_short, u_char *, int);
+
+typedef struct ppp_proto_struct {
+	int		proto;
+	pfn_proto	func;
+} ppp_proto_type;
+
+static
+ppp_proto_type proto_list[] = {
+	{ PPP_IP,         rcv_proto_ip         },
+	{ PPP_IPX,        rcv_proto_ipx        },
+	{ PPP_VJC_COMP,   rcv_proto_vjc_comp   },
+	{ PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp },
+	{ PPP_LQR,        rcv_proto_lqr        },
+	{ PPP_CCP,        rcv_proto_ccp        },
+	{ 0,              rcv_proto_unknown    }  /* !!! MUST BE LAST !!! */
+};
+
+/*
+ * Values for FCS calculations.
+ */
+
+#define PPP_INITFCS	0xffff	/* Initial FCS value */
+#define PPP_GOODFCS	0xf0b8	/* Good final FCS value */
+#define PPP_FCS(fcs, c)	(((fcs) >> 8) ^ ppp_crc16_table[((fcs) ^ (c)) & 0xff])
+
+unsigned short ppp_crc16_table[256] =
+{
+	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+#ifdef CHECK_CHARACTERS
+static unsigned paritytab[8] =
+{
+	0x96696996, 0x69969669, 0x69969669, 0x96696996,
+	0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+#endif
+
+/* local function to store a value into the LQR frame */
+extern inline u_char * store_long (register u_char *p, register int value) {
+	*p++ = (u_char) (value >> 24);
+	*p++ = (u_char) (value >> 16);
+	*p++ = (u_char) (value >>  8);
+	*p++ = (u_char) value;
+	return p;
+}
+
+/*************************************************************
+ * INITIALIZATION
+ *************************************************************/
+
+/* This procedure is called once and once only to define who we are to
+ * the operating system and the various procedures that it may use in
+ * accessing the ppp protocol.
+ */
+
+static int
+ppp_first_time (void)
+{
+	static struct tty_ldisc	ppp_ldisc;
+	int    status;
+
+	printk (KERN_INFO
+		"PPP: version %s (dynamic channel allocation)"
+#ifdef NEW_SKBUFF
+		" NEW_SKBUFF"
+#endif
+		"\n", szVersion);
+
+#ifndef MODULE /* slhc module logic has its own copyright announcment */
+	printk (KERN_INFO
+		"TCP compression code copyright 1989 Regents of the "
+		"University of California\n");
+#endif
+	
+	printk (KERN_INFO
+		"PPP Dynamic channel allocation code copyright 1995 "
+		"Caldera, Inc.\n");
+/*
+ * Register the protocol for the device
+ */
+
+#ifdef NEW_SKBUFF  
+	memset (&proto_ppp, 0, sizeof (proto_ppp));
+
+	proto_ppp.name          = "PPP";
+	proto_ppp.output        = ppp_dev_output;
+	proto_ppp.input         = ppp_dev_input;
+	proto_ppp.bh_input      = ppp_dev_input;
+	proto_ppp.control_event = default_protocol_control;
+	proto_ppp.get_binding   = ppp_dev_getkey;
+	proto_ppp.header_space  = 0; /* PPP_HARD_HDR_LEN; */
+
+	protocol_register(&proto_ppp);
+#endif
+
+/*
+ * Register the tty dicipline
+ */	
+	(void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
+	ppp_ldisc.magic		= TTY_LDISC_MAGIC;
+	ppp_ldisc.open		= ppp_tty_open;
+	ppp_ldisc.close		= ppp_tty_close;
+	ppp_ldisc.read		= ppp_tty_read;
+	ppp_ldisc.write		= ppp_tty_write;
+	ppp_ldisc.ioctl		= ppp_tty_ioctl;
+	ppp_ldisc.select	= ppp_tty_select;
+	ppp_ldisc.receive_room	= ppp_tty_room;
+	ppp_ldisc.receive_buf	= ppp_tty_receive;
+	ppp_ldisc.write_wakeup	= ppp_tty_wakeup;
+	
+	status = tty_register_ldisc (N_PPP, &ppp_ldisc);
+	if (status == 0)
+		printk (KERN_INFO "PPP line discipline registered.\n");
+	else
+		printk (KERN_ERR "error registering line discipline: %d\n",
+			status);
+	return status;
+}
+
+/*************************************************************
+ * INITIALIZATION
+ *************************************************************/
+
+/* called when the device is actually created */
+
+static int
+ppp_init_dev (struct device *dev)
+{
+	int    indx;
+#ifdef NEW_SKBUFF  
+	dev->default_protocol = &proto_ppp;	/* Our protocol layer is PPP */
+#else
+	dev->hard_header      = ppp_dev_header;
+#if USE_SKB_PROTOCOL == 0
+	dev->type_trans       = ppp_dev_type;
+#endif
+	dev->rebuild_header   = ppp_dev_rebuild;
+	dev->hard_header_len  = 0; /* PPP_HARD_HDR_LEN; */
+#endif
+
+	/* device INFO */
+	dev->mtu              = PPP_MTU;
+	dev->hard_start_xmit  = ppp_dev_xmit;
+	dev->open             = ppp_dev_open;
+	dev->stop             = ppp_dev_close;
+	dev->get_stats        = ppp_dev_stats;
+	dev->do_ioctl         = ppp_dev_ioctl;
+	dev->addr_len         = 0;
+	dev->type             = ARPHRD_PPP;
+
+	for (indx = 0; indx < DEV_NUMBUFFS; indx++)
+		skb_queue_head_init (&dev->buffs[indx]);
+
+	/* New-style flags */
+	dev->flags      = IFF_POINTOPOINT;
+	dev->family     = AF_INET;
+	dev->pa_addr    = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask    = 0;
+	dev->pa_alen    = 4; /* sizeof (unsigned long) */
+
+	return 0;
+}
+
+/*
+ * Local procedure to initialize the ppp structure
+ */
+
+static void
+ppp_init_ctrl_blk (register struct ppp *ppp)
+{
+	ppp->magic  = PPP_MAGIC;
+	ppp->toss   = 0xE0;
+	ppp->escape = 0;
+
+	ppp->flags  = 0;
+	ppp->mtu    = PPP_MTU;
+	ppp->mru    = PPP_MRU;
+
+	memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
+	ppp->xmit_async_map[0] = 0xffffffff;
+	ppp->xmit_async_map[3] = 0x60000000;
+	ppp->recv_async_map    = 0x00000000;
+
+	ppp->rbuf       = NULL;
+	ppp->wbuf       = NULL;
+	ppp->ubuf       = NULL;
+	ppp->cbuf       = NULL;
+	ppp->slcomp     = NULL;
+	ppp->read_wait  = NULL;
+	ppp->write_wait = NULL;
+	ppp->last_xmit  = jiffies - flag_time;
+
+	/* clear statistics */
+	memset (&ppp->stats, '\0', sizeof (struct pppstat));
+
+	/* Reset the demand dial information */
+	ppp->ddinfo.xmit_idle=         /* time since last NP packet sent */
+	ppp->ddinfo.recv_idle=jiffies; /* time since last NP packet received */
+
+	/* PPP compression data */
+	ppp->sc_xc_state =
+	ppp->sc_rc_state = NULL;
+}
+
+static struct symbol_table ppp_syms = {
+#include <linux/symtab_begin.h>
+	X(ppp_register_compressor),
+	X(ppp_unregister_compressor),
+	X(ppp_crc16_table),
+#include <linux/symtab_end.h>
+};
+
+/* called at boot/load time for each ppp device defined in the kernel */
+
+#ifndef MODULE
+int
+ppp_init (struct device *dev)
+{
+	static int first_time = 1;
+	int    answer = 0;
+
+	if (first_time) {
+		first_time = 0;
+		answer     = ppp_first_time();
+		if (answer == 0)
+			(void) register_symtab (&ppp_syms);
+	}
+	if (answer)
+		return answer;
+	/*
+	 * Return "not found", so that dev_init() will unlink
+	 * the placeholder device entry for us.
+	 */
+	return ENODEV;
+
+}
+#endif
+
+/*
+ * Routine to allocate a buffer for later use by the driver.
+ */
+
+static struct ppp_buffer *
+ppp_alloc_buf (int size, int type)
+{
+	struct ppp_buffer *buf;
+
+	buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer),
+					     GFP_ATOMIC);
+
+	if (buf != NULL) {
+		buf->size   = size - 1;	/* Mask for the buffer size */
+		buf->type   = type;
+		buf->locked = 0;
+		buf->count  = 0;
+		buf->head   = 0;
+		buf->tail   = 0;
+		buf->fcs    = PPP_INITFCS;
+	}
+	return (buf);
+}
+
+/*
+ * Routine to release the allocated buffer.
+ */
+
+static void
+ppp_free_buf (struct ppp_buffer *ptr)
+{
+	if (ptr != NULL)
+		kfree (ptr);
+}
+
+/*
+ * Lock the indicated transmit buffer
+ */
+
+extern inline int
+lock_buffer (register struct ppp_buffer *buf)
+{
+	register int state;
+	int flags;
+/*
+ * Save the current state and if free then set it to the "busy" state
+ */
+	save_flags (flags);
+	cli ();
+	state = buf->locked;
+	if (state == 0)
+		buf->locked = 2;
+/*
+ * Restore the flags and return the previous state. 0 implies success.
+ */
+	restore_flags (flags);
+	return (state);
+}
+
+/*
+ * MTU has been changed by the IP layer. Unfortunately we are not told
+ * about this, but we spot it ourselves and fix things up. We could be
+ * in an upcall from the tty driver, or in an ip packet queue.
+ */
+
+static int
+ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
+{
+	struct device *dev;
+
+	struct ppp_buffer *new_rbuf;
+	struct ppp_buffer *new_wbuf;
+	struct ppp_buffer *new_cbuf;
+	struct ppp_buffer *new_tbuf;
+
+	struct ppp_buffer *old_rbuf;
+	struct ppp_buffer *old_wbuf;
+	struct ppp_buffer *old_cbuf;
+	struct ppp_buffer *old_tbuf;
+
+	int mtu, mru;
+/*
+ *  Allocate the buffer from the kernel for the data
+ */
+	dev = ppp2dev (ppp);
+	mru = new_mru;
+	/* allow for possible escapement of every character */
+	mtu = (new_mtu * 2) + 20;
+
+	/* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
+	if (mru < PPP_MRU)
+		mru = PPP_MRU;
+
+	mru += 10;
+	
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n",
+			dev->name, new_mtu, new_mru);
+
+	new_wbuf = ppp_alloc_buf (mtu+PPP_HARD_HDR_LEN,	BUFFER_TYPE_DEV_WR);
+	new_tbuf = ppp_alloc_buf ((PPP_MTU * 2) + 24,	BUFFER_TYPE_TTY_WR);
+	new_rbuf = ppp_alloc_buf (mru + 84,		BUFFER_TYPE_DEV_RD);
+	new_cbuf = ppp_alloc_buf (mru+PPP_HARD_HDR_LEN,	BUFFER_TYPE_VJ);
+/*
+ *  If the buffers failed to allocate then complain and release the partial
+ *  allocations.
+ */
+	if (new_wbuf == NULL || new_tbuf == NULL ||
+	    new_rbuf == NULL || new_cbuf == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+				"ppp: failed to allocate new buffers\n");
+
+		ppp_free_buf (new_wbuf);
+		ppp_free_buf (new_tbuf);
+		ppp_free_buf (new_rbuf);
+		ppp_free_buf (new_cbuf);
+		return 0;
+	}
+/*
+ *  Update the pointers to the new buffer structures.
+ */
+	cli ();
+	old_wbuf = ppp->wbuf;
+	old_rbuf = ppp->rbuf;
+	old_cbuf = ppp->cbuf;
+	old_tbuf = ppp->tbuf;
+
+	ppp->wbuf = new_wbuf;
+	ppp->rbuf = new_rbuf;
+	ppp->cbuf = new_cbuf;
+	ppp->tbuf = new_tbuf;
+
+	ppp->rbuf->size -= 80;  /* reserve space for vj header expansion */
+
+	dev->mem_start  = (unsigned long) buf_base (new_wbuf);
+	dev->mem_end    = (unsigned long) (dev->mem_start + mtu);
+	dev->rmem_start = (unsigned long) buf_base (new_rbuf);
+	dev->rmem_end   = (unsigned long) (dev->rmem_start + mru);
+/*
+ *  Update the parameters for the new buffer sizes
+ */
+	ppp->toss   = 0xE0;	/* To ignore characters until new FLAG */
+	ppp->escape = 0;	/* No pending escape character */
+
+	dev->mtu    =
+	ppp->mtu    = new_mtu;
+	ppp->mru    = new_mru;
+
+	ppp->s1buf  = NULL;
+	ppp->s2buf  = NULL;
+	ppp->xbuf   = NULL;
+
+	ppp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	ppp->flags      &= ~SC_XMIT_BUSY;
+
+	sti ();
+/*
+ *  Release old buffer pointers
+ */
+	ppp_free_buf (old_rbuf);
+	ppp_free_buf (old_wbuf);
+	ppp_free_buf (old_cbuf);
+	ppp_free_buf (old_tbuf);
+	return 1;
+}
+
+/*
+ * CCP is down; free (de)compressor state if necessary.
+ */
+
+static void
+ppp_ccp_closed (struct ppp *ppp)
+{
+	if (ppp->sc_xc_state) {
+		(*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
+		ppp->sc_xc_state = NULL;
+	}
+
+	if (ppp->sc_rc_state) {
+		(*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
+		ppp->sc_rc_state = NULL;
+	}
+}
+
+/*
+ * Called to release all of the information in the current PPP structure.
+ *
+ * It is called when the ppp device goes down or if it is unable to go
+ * up.
+ */
+
+static void
+ppp_release (struct ppp *ppp)
+{
+	struct tty_struct *tty;
+	struct device *dev;
+
+	tty = ppp2tty (ppp);
+	dev = ppp2dev (ppp);
+
+	ppp_ccp_closed (ppp);
+
+	if (tty != NULL && tty->disc_data == ppp)
+		tty->disc_data = NULL;	/* Break the tty->ppp link */
+
+	if (dev && dev->flags & IFF_UP) {
+		dev_close (dev); /* close the device properly */
+		dev->flags = 0;  /* prevent recursion */
+	}
+
+	ppp_free_buf (ppp->rbuf);
+	ppp_free_buf (ppp->wbuf);
+	ppp_free_buf (ppp->cbuf);
+	ppp_free_buf (ppp->ubuf);
+	ppp_free_buf (ppp->tbuf);
+
+	ppp->rbuf  =
+	ppp->wbuf  =
+	ppp->cbuf  =
+	ppp->tbuf  =
+	ppp->xbuf  =
+	ppp->s1buf =
+	ppp->s2buf =
+	ppp->ubuf  = NULL;
+
+	if (ppp->slcomp) {
+		slhc_free (ppp->slcomp);
+		ppp->slcomp = NULL;
+	}
+
+	ppp->inuse = 0;
+	ppp->tty   = NULL;
+}
+
+/*
+ * Device callback.
+ *
+ * Called when the PPP device goes down in response to an ifconfig request.
+ */
+
+static void
+ppp_tty_close (struct tty_struct *tty)
+{
+	struct ppp *ppp = tty2ppp (tty);
+
+	if (ppp != NULL) {
+		if (ppp->magic != PPP_MAGIC) {
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_WARNING
+				       "ppp: trying to close unopened tty!\n");
+		} else {
+			CHECK_PPP_VOID();
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO "ppp: channel %s closing.\n",
+					ppp2dev(ppp) -> name);
+			ppp_release (ppp);
+			MOD_DEC_USE_COUNT;
+		}
+	}
+}
+
+/*
+ * TTY callback.
+ *
+ * Called when the tty discipline is switched to PPP.
+ */
+
+static int
+ppp_tty_open (struct tty_struct *tty)
+{
+	struct ppp *ppp = tty2ppp (tty);
+/*
+ * There should not be an existing table for this slot.
+ */
+	if (ppp) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+			"ppp_tty_open: gack! tty already associated to %s!\n",
+			ppp->magic == PPP_MAGIC ? ppp2dev(ppp)->name
+						: "unknown");
+		return -EEXIST;
+	}
+/*
+ * Allocate the structure from the system
+ */
+	ppp = ppp_alloc();
+	if (ppp == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+			"ppp_tty_open: couldn't allocate ppp channel\n");
+		return -ENFILE;
+	}
+/*
+ * Initialize the control block
+ */
+	ppp_init_ctrl_blk (ppp);
+	ppp->tty       = tty;
+	tty->disc_data = ppp;
+/*
+ * Flush any pending characters in the driver and discipline.
+ */
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer (tty);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer (tty);
+/*
+ * Allocate space for the default VJ header compression slots
+ */
+	ppp->slcomp = slhc_init (16, 16);
+	if (ppp->slcomp == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+			"ppp_tty_open: no space for compression buffers!\n");
+		ppp_release (ppp);
+		return -ENOMEM;
+	}
+/*
+ * Allocate space for the MTU and MRU buffers
+ */
+	if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) {
+		ppp_release (ppp);
+		return -ENOMEM;
+	}
+/*
+ * Allocate space for a user level buffer
+ */
+	ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD);
+	if (ppp->ubuf == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+		       "ppp_tty_open: no space for user receive buffer\n");
+		ppp_release (ppp);
+		return -ENOMEM;
+	}
+
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_INFO "ppp: channel %s open\n",
+			ppp2dev(ppp)->name);
+
+	MOD_INC_USE_COUNT;
+	return (ppp->line);
+}
+
+/*
+ * Local function to send the next portion of the buffer.
+ *
+ * Called by the tty driver's tty_wakeup function should it be entered
+ * because the partial buffer was transmitted.
+ *
+ * Called by kick_tty to send the initial portion of the buffer.
+ *
+ * Completion processing of the buffer transmission is handled here.
+ */
+
+static void
+ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty,
+		     struct ppp_buffer *xbuf)
+{
+	register int count, actual;
+/*
+ * Prevent re-entrancy by ensuring that this routine is called only once.
+ */
+	cli ();
+	if (ppp->flags & SC_XMIT_BUSY) {
+		sti ();
+		return;
+	}
+	ppp->flags |= SC_XMIT_BUSY;
+	sti ();
+/*
+ * Send the next block of data to the modem
+ */
+	count = xbuf->count - xbuf->tail;
+	actual = tty->driver.write (tty, 0,
+				    buf_base (xbuf) + xbuf->tail, count);
+/*
+ * Terminate transmission of any block which may have an error.
+ * This could occur should the carrier drop.
+ */
+	if (actual < 0) {
+	        ppp->stats.ppp_oerrors++;
+		actual = count;
+	} else
+		ppp->bytes_sent += actual;
+/*
+ * If the buffer has been transmitted then clear the indicators.
+ */
+	xbuf->tail += actual;
+	if (actual == count) {
+		xbuf = NULL;
+		ppp->flags &= ~SC_XMIT_BUSY;
+/*
+ * Complete the transmission on the current buffer.
+ */
+		xbuf = ppp->xbuf;
+		if (xbuf != NULL) {
+			tty->flags  &= ~(1 << TTY_DO_WRITE_WAKEUP);
+			xbuf->locked = 0;
+			ppp->xbuf    = NULL;
+/*
+ * If the completed buffer came from the device write, then complete the
+ * transmission block.
+ */
+			if (ppp2dev (ppp) -> flags & IFF_UP) {
+				if (xbuf->type == BUFFER_TYPE_DEV_WR)
+				        ppp2dev (ppp)->tbusy = 0;
+				mark_bh (NET_BH);
+			}
+/*
+ * Wake up the transmission queue for all completion events.
+ */
+			wake_up_interruptible (&ppp->write_wait);
+/*
+ * Look at the priorities. Choose a daemon write over the device driver.
+ */
+			cli();
+			xbuf = ppp->s1buf;
+			ppp->s1buf = NULL;
+			if (xbuf == NULL) {
+				xbuf = ppp->s2buf;
+				ppp->s2buf = NULL;
+			}
+			sti();
+/*
+ * If there is a pending buffer then transmit it now.
+ */
+			if (xbuf != NULL) {
+				ppp->flags &= ~SC_XMIT_BUSY;
+				ppp_kick_tty (ppp, xbuf);
+				return;
+			}
+		}
+	}
+/*
+ * Clear the re-entry flag
+ */
+	ppp->flags &= ~SC_XMIT_BUSY;
+}
+
+/*
+ * This function is called by the tty driver when the transmit buffer has
+ * additional space. It is used by the ppp code to continue to transmit
+ * the current buffer should the buffer have been partially sent.
+ *
+ * In addition, it is used to send the first part of the buffer since the
+ * logic and the inter-locking would be identical.
+ */
+
+static void
+ppp_tty_wakeup (struct tty_struct *tty)
+{
+	struct ppp_buffer *xbuf;
+	struct ppp *ppp = tty2ppp (tty);
+
+	if (!ppp)
+		return;
+
+	if (ppp->magic != PPP_MAGIC)
+		return;
+/*
+ * Ensure that there is a transmission pending. Clear the re-entry flag if
+ * there is no pending buffer. Otherwise, send the buffer.
+ */
+	xbuf = ppp->xbuf;
+	if (xbuf == NULL)
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	else
+		ppp_tty_wakeup_code (ppp, tty, xbuf);
+}
+
+/*
+ * This function is called to transmit a buffer to the remote. The buffer
+ * is placed on the pending queue if there is presently a buffer being
+ * sent or it is transmitted with the aid of ppp_tty_wakeup.
+ */
+
+static void
+ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf)
+{
+	register int flags;
+/*
+ * Hold interrupts.
+ */
+	save_flags (flags);
+	cli ();
+/*
+ * Control the flags which are best performed with the interrupts masked.
+ */
+	xbuf->locked     = 1;
+	xbuf->tail       = 0;
+/*
+ * If the transmitter is busy then place the buffer on the appropriate
+ * priority queue.
+ */
+	if (ppp->xbuf != NULL) {
+		if (xbuf->type == BUFFER_TYPE_TTY_WR)
+			ppp->s1buf = xbuf;
+		else
+			ppp->s2buf = xbuf;
+		restore_flags (flags);
+		return;
+	}
+/*
+ * If the transmitter is not busy then this is the highest priority frame
+ */
+	ppp->flags      &= ~SC_XMIT_BUSY;
+	ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+	ppp->xbuf        = xbuf;
+	restore_flags (flags);
+/*
+ * Do the "tty wakeup_code" to actually send this buffer.
+ */
+	ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf);
+}
+
+/*************************************************************
+ * TTY INPUT
+ *    The following functions handle input that arrives from
+ *    the TTY.	It recognizes PPP frames and either hands them
+ *    to the network layer or queues them for delivery to a
+ *    user process reading this TTY.
+ *************************************************************/
+
+/*
+ * Callback function from tty driver. Return the amount of space left
+ * in the receiver's buffer to decide if remote transmitter is to be
+ * throttled.
+ */
+
+static int
+ppp_tty_room (struct tty_struct *tty)
+{
+	return 65536;	    /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Callback function when data is available at the tty driver.
+ */
+
+static void
+ppp_tty_receive (struct tty_struct *tty, const u_char * data,
+		 char *flags, int count)
+{
+	register struct ppp *ppp = tty2ppp (tty);
+	register struct ppp_buffer *buf = NULL;
+	u_char chr;
+/*
+ * Fetch the pointer to the buffer. Be careful about race conditions.
+ */
+	if (ppp != NULL)
+		buf = ppp->rbuf;
+
+	if (buf == NULL)
+		return;
+/*
+ * Verify the table pointer and ensure that the line is
+ * still in PPP discipline.
+ */
+	if (ppp->magic != PPP_MAGIC) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_DEBUG
+				"PPP: handler called but couldn't find "
+				"PPP struct.\n");
+		return;
+	}
+	CHECK_PPP_VOID ();
+/*
+ * Print the buffer if desired
+ */
+	if (ppp->flags & SC_LOG_RAWIN)
+		ppp_print_buffer ("receive buffer", data, count);
+/*
+ * Collect the character and error condition for the character. Set the toss
+ * flag for the first character error.
+ */
+	while (count-- > 0) {
+		ppp->bytes_rcvd++;
+		chr = *data++;
+		if (flags) {
+			if (*flags && ppp->toss == 0)
+				ppp->toss = *flags;
+			++flags;
+		}
+/*
+ * Set the flags for 8 data bits and no parity.
+ *
+ * Actually, it sets the flags for d7 being 0/1 and parity being even/odd
+ * so that the normal processing would have all flags set at the end of the
+ * session. A missing flag bit would denote an error condition.
+ */
+
+#ifdef CHECK_CHARACTERS
+		if (chr & 0x80)
+			ppp->flags |= SC_RCV_B7_1;
+		else
+			ppp->flags |= SC_RCV_B7_0;
+
+		if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
+			ppp->flags |= SC_RCV_ODDP;
+		else
+			ppp->flags |= SC_RCV_EVNP;
+#endif
+/*
+ * Branch on the character. Process the escape character. The sequence ESC ESC
+ * is defined to be ESC.
+ */
+		switch (chr) {
+		case PPP_ESCAPE: /* PPP_ESCAPE: invert bit in next character */
+			ppp->escape = PPP_TRANS;
+			break;
+/*
+ * FLAG. This is the end of the block. If the block terminated by ESC FLAG,
+ * then the block is to be ignored. In addition, characters before the very
+ * first FLAG are also tossed by this procedure.
+ */
+		case PPP_FLAG:	/* PPP_FLAG: end of frame */
+			ppp->stats.ppp_ibytes += ppp->rbuf->count;
+			if (ppp->escape)
+				ppp->toss |= 0x80;
+/*
+ * Process frames which are not to be ignored. If the processing failed,
+ * then clean up the VJ tables.
+ */
+			if ((ppp->toss & 0x80) != 0 ||
+			    ppp_doframe (ppp) == 0) {
+				slhc_toss (ppp->slcomp);
+			}
+/*
+ * Reset all indicators for the new frame to follow.
+ */
+			buf->count  = 0;
+			buf->fcs    = PPP_INITFCS;
+			ppp->escape = 0;
+			ppp->toss   = 0;
+			break;
+/*
+ * All other characters in the data come here. If the character is in the
+ * receive mask then ignore the character.
+ */
+		default:
+			if (in_rmap (ppp, chr))
+				break;
+/*
+ * Adjust the character and if the frame is to be discarded then simply
+ * ignore the character until the ending FLAG is received.
+ */
+			chr ^= ppp->escape;
+			ppp->escape = 0;
+
+			if (ppp->toss != 0)
+				break;
+/*
+ * If the count sent is within reason then store the character, bump the
+ * count, and update the FCS for the character.
+ */
+			if (buf->count < buf->size) {
+				buf_base (buf)[buf->count++] = chr;
+				buf->fcs = PPP_FCS (buf->fcs, chr);
+				break;
+			}
+/*
+ * The peer sent too much data. Set the flags to discard the current frame
+ * and wait for the re-synchronization FLAG to be sent.
+ */
+			ppp->stats.ppp_ierrors++;
+			ppp->toss |= 0xC0;
+			break;
+		}
+	}
+}
+
+/*
+ * Put the input frame into the networking system for the indicated protocol
+ */
+
+static int
+ppp_rcv_rx (struct ppp *ppp, unsigned short proto, u_char * data, int count)
+{
+	sk_buff *skb = dev_alloc_skb (count);
+/*
+ * Generate a skb buffer for the new frame.
+ */
+	if (skb == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+			 "ppp_do_ip: packet dropped on %s (no memory)!\n",
+			 ppp2dev (ppp)->name);
+		return 0;
+	}
+/*
+ * Move the received data from the input buffer to the skb buffer.
+ */
+	skb->dev = ppp2dev (ppp);	/* We are the device */
+#if USE_SKB_PROTOCOL == 0
+	skb->len = count;
+#else
+	skb->protocol = proto;
+	skb->mac.raw  = skb_data(skb);
+#endif
+	memcpy (skb_put(skb,count), data, count);	/* move data */
+/*
+ * Tag the frame and kick it to the proper receive routine
+ */
+	skb->free = 1;
+	ppp->ddinfo.recv_idle = jiffies;
+	netif_rx (skb);
+	return 1;
+}
+
+/*
+ * Process the receipt of an IP frame
+ */
+
+static int
+rcv_proto_ip (struct ppp *ppp, unsigned short proto, u_char * data, int count)
+{
+	if (ppp2dev (ppp)->flags & IFF_UP) {
+		if (count > 0)
+			return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count);
+	}
+	return 0;
+}
+
+/*
+ * Process the receipt of an IPX frame
+ */
+
+static int
+rcv_proto_ipx (struct ppp *ppp, unsigned short proto, u_char * data, int count)
+{
+#ifdef NEW_SKBUFF
+	if (ppp2dev (ppp)->flags & IFF_UP) {
+		if (count > 0)
+			return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count);
+	} else
+#endif
+	return 0;
+}
+
+/*
+ * Process the receipt of an VJ Compressed frame
+ */
+
+static int
+rcv_proto_vjc_comp (struct ppp *ppp, unsigned short proto,
+		    u_char *data, int count)
+{
+	if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
+		int new_count = slhc_uncompress (ppp->slcomp, data, count);
+		if (new_count >= 0) {
+			return rcv_proto_ip (ppp, PPP_IP, data, new_count);
+		}
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_NOTICE
+				"ppp: error in VJ decompression\n");
+	}
+	return 0;
+}
+
+/*
+ * Process the receipt of an VJ Un-compressed frame
+ */
+
+static int
+rcv_proto_vjc_uncomp (struct ppp *ppp, unsigned short proto,
+		      u_char *data, int count)
+{
+	if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
+		if (slhc_remember (ppp->slcomp, data, count) > 0) {
+			return rcv_proto_ip (ppp, PPP_IP, data, count);
+		}
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_NOTICE
+				"ppp: error in VJ memorizing\n");
+	}
+	return 0;
+}
+
+/*
+ * Receive all unclassified protocols.
+ */
+
+static int
+rcv_proto_unknown (struct ppp *ppp, unsigned short proto,
+		   u_char *data, int len)
+{
+	int totlen;
+	register int current_idx;
+
+#define PUTC(c)						 \
+{							 \
+    buf_base (ppp->ubuf) [current_idx++] = (u_char) (c); \
+    current_idx &= ppp->ubuf->size;			 \
+    if (current_idx == ppp->ubuf->tail)			 \
+	    goto failure;				 \
+}
+
+/*
+ * The total length includes the protocol data.
+ * Lock the user information buffer.
+ */
+	if (set_bit (0, &ppp->ubuf->locked)) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_DEBUG
+				"ppp_us_queue: can't get lock\n");
+	} else {
+		current_idx = ppp->ubuf->head;
+/*
+ * Insert the buffer length (not counted), the protocol, and the data
+ */
+		totlen = len + 2;
+		PUTC (totlen >> 8);
+		PUTC (totlen);
+
+		PUTC (proto >> 8);
+		PUTC (proto);
+
+		totlen -= 2;
+		while (totlen-- > 0) {
+			PUTC (*data++);
+		}
+#undef PUTC
+/*
+ * The frame is complete. Update the head pointer and wakeup the pppd
+ * process.
+ */
+		ppp->ubuf->head = current_idx;
+
+		clear_bit (0, &ppp->ubuf->locked);
+		wake_up_interruptible (&ppp->read_wait);
+		if (ppp->tty->fasync != NULL)
+			kill_fasync (ppp->tty->fasync, SIGIO);
+
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_INFO
+				"ppp: successfully queued %d bytes, flags = %x\n",
+				len + 2, ppp->flags);
+
+		return 1;
+/*
+ * The buffer is full. Unlock the header
+ */
+failure:
+		clear_bit (0, &ppp->ubuf->locked);
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_INFO
+				"ppp_us_queue: ran out of buffer space.\n");
+	}
+/*
+ * Discard the frame. There are no takers for this protocol.
+ */
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_WARNING
+			"ppp: dropping packet on the floor.\n");
+	slhc_toss (ppp->slcomp);
+	return 0;
+}
+
+/*
+ * Handle a CCP packet.
+ *
+ * The CCP packet is passed along to the pppd process just like any
+ * other PPP frame. The difference is that some processing needs to be
+ * immediate or the compressors will become confused on the peer.
+ */
+
+static void ppp_proto_ccp (struct ppp *ppp, u_char *dp, int len, int rcvd)
+{
+	int slen    = CCP_LENGTH(dp);
+	u_char *opt = dp   + CCP_HDRLEN;
+	int opt_len = slen - CCP_HDRLEN;
+
+	if (slen > len)
+		return;
+
+	switch (CCP_CODE(dp)) {
+	case CCP_CONFREQ:
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+/*
+ * CCP must be going down - disable compression
+ */
+		if (ppp->flags & SC_CCP_UP) {
+			ppp->flags &= ~(SC_CCP_UP   |
+					SC_COMP_RUN |
+					SC_DECOMP_RUN);
+		}
+		break;
+
+	case CCP_CONFACK:
+		if ((ppp->flags & SC_CCP_OPEN) == 0)
+			break;
+		if (ppp->flags & SC_CCP_UP)
+			break;
+		if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
+			break;
+		if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
+			break;
+/*
+ * we're agreeing to send compressed packets.
+ */
+		if (!rcvd) {
+			if (ppp->sc_xc_state == NULL)
+				break;
+
+			if ((*ppp->sc_xcomp->comp_init)
+			    (ppp->sc_xc_state,
+			     opt,
+			     opt_len,
+			     ppp2dev (ppp)->base_addr,
+			     0,
+			     ppp->flags & SC_DEBUG))
+				ppp->flags |= SC_COMP_RUN;
+			break;
+		}
+/*
+ * peer is agreeing to send compressed packets.
+ */
+		if (ppp->sc_rc_state == NULL)
+			break;
+
+		if ((*ppp->sc_rcomp->decomp_init)
+		    (ppp->sc_rc_state,
+		     opt,
+		     opt_len,
+		     ppp2dev (ppp)->base_addr,
+		     0,
+		     ppp->mru,
+		     ppp->flags & SC_DEBUG)) {
+			ppp->flags |= SC_DECOMP_RUN;
+			ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
+		}
+		break;
+/*
+ * The protocol sequence is complete at this end
+ */
+	case CCP_RESETACK:
+		if ((ppp->flags & SC_CCP_UP) == 0)
+			break;
+
+		if (!rcvd) {
+			if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN))
+				(*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
+		} else {
+			if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
+			      (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
+				ppp->flags &= ~SC_DC_ERROR;
+			}
+		}
+		break;
+	}
+}
+
+static int
+rcv_proto_ccp (struct ppp *ppp, unsigned short proto, u_char *dp, int len)
+{
+	ppp_proto_ccp (ppp, dp, len, 1);
+	return rcv_proto_unknown (ppp, proto, dp, len);
+}
+
+/*
+ * Handle a LQR packet.
+ *
+ * The LQR packet is passed along to the pppd process just like any
+ * other PPP frame. The difference is that some processing needs to be
+ * performed to append the current data to the end of the frame.
+ */
+
+static int
+rcv_proto_lqr (struct ppp *ppp, unsigned short proto, u_char * data, int len)
+{
+#if 0 /* until support is in the pppd process don't corrupt the reject. */
+	register u_char *p;
+	if (len > 8) {
+		if (len < 48)
+			memset (&data [len], '\0', 48 - len);
+/*
+ * Fill in the fields from the driver data
+ */
+		p = &data [48];
+		p = store_long (p, ++ppp->stats.ppp_ilqrs);
+		p = store_long (p, ppp->stats.ppp_ipackets);
+		p = store_long (p, ppp->stats.ppp_discards);
+		p = store_long (p, ppp->stats.ppp_ierrors);
+		p = store_long (p, ppp->stats.ppp_ioctects + len);
+
+		len = 68;
+	}
+#endif
+/*
+ * Pass the frame to the pppd daemon.
+ */
+	return rcv_proto_unknown (ppp, proto, data, len);
+}
+
+/* on entry, a received frame is in ppp->rbuf.bufr
+   check it and dispose as appropriate */
+
+static void ppp_doframe_lower (struct ppp *ppp, u_char *data, int count)
+{
+	u_short		proto = PPP_PROTOCOL (data);
+	ppp_proto_type  *proto_ptr;
+/*
+ * Ignore empty frames
+ */
+	if (count <= 4)
+		return;
+/*
+ * Count the frame and print it
+ */
+	++ppp->stats.ppp_ipackets;
+	if (ppp->flags & SC_LOG_INPKT)
+		ppp_print_buffer ("receive frame", data, count);
+/*
+ * Find the procedure to handle this protocol. The last one is marked
+ * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon.
+ */
+	proto_ptr = proto_list;
+	while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
+		++proto_ptr;
+/*
+ * Update the appropriate statistic counter.
+ */
+	if ((*proto_ptr->func) (ppp, proto,
+				&data[PPP_HARD_HDR_LEN],
+				count - PPP_HARD_HDR_LEN))
+		ppp->stats.ppp_ioctects += count;
+	else
+		++ppp->stats.ppp_discards;
+}
+
+/* on entry, a received frame is in ppp->rbuf.bufr
+   check it and dispose as appropriate */
+
+static int
+ppp_doframe (struct ppp *ppp)
+{
+	u_char	*data = buf_base (ppp->rbuf);
+	int	count = ppp->rbuf->count;
+	int	addr, ctrl, proto;
+	int	new_count;
+	u_char *new_data;
+#ifndef NO_RMK_TEMP_FIX
+	int	malloced_data;
+#endif
+/*
+ * If there is a pending error from the receiver then log it and discard
+ * the damaged frame.
+ */
+	if (ppp->toss) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_WARNING
+				"ppp_toss: tossing frame, reason = %d\n",
+				ppp->toss);
+		ppp->stats.ppp_ierrors++;
+		return 0;
+	}
+/*
+ * An empty frame is ignored. This occurs if the FLAG sequence precedes and
+ * follows each frame.
+ */
+	if (count == 0)
+		return 1;
+/*
+ * Generate an error if the frame is too small.
+ */
+	if (count < PPP_HARD_HDR_LEN) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_WARNING
+				"ppp: got runt ppp frame, %d chars\n", count);
+		slhc_toss (ppp->slcomp);
+		ppp->stats.ppp_ierrors++;
+		return 1;
+	}
+/*
+ * Verify the CRC of the frame and discard the CRC characters from the
+ * end of the buffer.
+ */
+	if (ppp->rbuf->fcs != PPP_GOODFCS) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_WARNING
+				"ppp: frame with bad fcs, excess = %x\n",
+				ppp->rbuf->fcs ^ PPP_GOODFCS);
+		ppp->stats.ppp_ierrors++;
+		return 0;
+	}
+	count -= 2;		/* ignore the fcs characters */
+/*
+ * Ignore the leading ADDRESS and CONTROL fields in the frame.
+ */
+	addr   = PPP_ALLSTATIONS;
+	ctrl   = PPP_UI;
+
+	if ((data[0] == PPP_ALLSTATIONS) && (data[1] == PPP_UI)) {
+		data  += 2;
+		count -= 2;
+	}
+/*
+ * Obtain the protocol from the frame
+ */
+	proto = (u_short) *data++;
+	if ((proto & 1) == 0) {
+		proto = (proto << 8) | (u_short) *data++;
+		--count;
+	}
+/*
+ * Rewrite the header with the full information. This may encroach upon
+ * the 'filler' area in the buffer header. This is the purpose for the
+ * filler.
+ */
+	*(--data) = proto;
+	*(--data) = proto >> 8;
+	*(--data) = ctrl;
+	*(--data) = addr;
+	count    += 3;
+#ifndef NO_RMK_TEMP_FIX
+	/*
+	 * rmk: should make sure that the packet received is on a word boundary.
+	 * this is a temporary fix!!!
+	 */
+	if ((int)data & 3) {
+		new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC); /* would like to have 'count' here */
+		memcpy (new_data, data, count);
+		data = new_data;
+		malloced_data = 1;
+	} else
+		malloced_data = 0;
+#endif
+/*
+ * Process the active decompressor.
+ */
+	if ((ppp->sc_rc_state != (void *) 0) &&
+	    (ppp->flags & SC_DECOMP_RUN)     &&
+	    ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
+		if (proto == PPP_COMP) {
+/*
+ * If the frame is compressed then decompress it.
+ */
+			new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC);
+			if (new_data == NULL) {
+				if (ppp->flags & SC_DEBUG)
+					printk (KERN_ERR
+						"ppp_doframe: no memory\n");
+				slhc_toss (ppp->slcomp);
+				(*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
+							  data,
+							  count);
+#ifndef NO_RMK_TEMP_FIX
+				if (malloced_data) kfree (data);
+#endif							  
+				return 1;
+			}
+/*
+ * Decompress the frame
+ */
+			new_count = bsd_decompress (ppp->sc_rc_state,
+						    data,
+						    count,
+						    new_data,
+						    ppp->mru + 4);
+			switch (new_count) {
+			default:
+				ppp_doframe_lower (ppp, new_data, new_count);
+				kfree (new_data);
+#ifndef NO_RMK_TEMP_FIX
+				if (malloced_data) kfree (data);
+#endif
+				return 1;
+
+			case DECOMP_OK:
+				break;
+
+			case DECOMP_ERROR:
+				ppp->flags |= SC_DC_ERROR;
+				break;
+
+			case DECOMP_FATALERROR:
+				ppp->flags |= SC_DC_FERROR;
+				break;
+			}
+/*
+ * Log the error condition and discard the frame.
+ */
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_ERR
+					"ppp_proto_comp: "
+					"decompress err %d\n", new_count);
+			kfree (new_data);
+			slhc_toss (ppp->slcomp);
+#ifndef NO_RMK_TEMP_FIX
+			if (malloced_data) kfree (data);
+#endif							  
+			return 1;
+		}
+/*
+ * The frame is not special. Pass it through the compressor without
+ * actually compressing the data
+ */
+		(*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
+					  data,
+					  count);
+	}
+/*
+ * Process the uncompressed frame.
+ */
+	ppp_doframe_lower (ppp, data, count);
+#ifndef NO_RMK_TEMP_FIX
+	if (malloced_data) kfree (data);
+#endif							  
+	return 1;
+}
+
+/*************************************************************
+ * LINE DISCIPLINE SUPPORT
+ *    The following functions form support user programs
+ *    which read and write data on a TTY with the PPP line
+ *    discipline.  Reading is done from a circular queue,
+ *    filled by the lower TTY levels.
+ *************************************************************/
+
+/* read a PPP frame from the us_rbuff circular buffer,
+   waiting if necessary
+*/
+
+static int
+ppp_tty_read (struct tty_struct *tty, struct file *file, u_char * buf,
+	      unsigned int nr)
+{
+	struct ppp *ppp = tty2ppp (tty);
+	u_char c;
+	int len, indx;
+
+#define GETC(c)						\
+{							\
+	c = buf_base (ppp->ubuf) [ppp->ubuf->tail++];	\
+	ppp->ubuf->tail &= ppp->ubuf->size;		\
+}
+
+/*
+ * Validate the pointer to the PPP structure
+ */
+	if (!ppp)
+		return -EIO;
+
+	if (ppp->magic != PPP_MAGIC)
+		return -EIO;
+
+	CHECK_PPP (-ENXIO);
+
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_DEBUG
+			"ppp_tty_read: called buf=%p nr=%u\n",
+			buf, nr);
+/*
+ * Acquire the read lock.
+ */
+	for (;;) {
+		ppp = tty2ppp (tty);
+		if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
+			return 0;
+
+		if (set_bit (0, &ppp->ubuf->locked) != 0) {
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_DEBUG
+				     "ppp_tty_read: sleeping(ubuf)\n");
+
+			current->timeout = 0;
+			current->state   = TASK_INTERRUPTIBLE;
+			schedule ();
+
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			continue;
+		}
+/*
+ * Before we attempt to write the frame to the user, ensure that the
+ * user has access to the pages for the total buffer length.
+ */
+		indx = verify_area (VERIFY_WRITE, buf, nr);
+		if (indx != 0)
+			return (indx);
+/*
+ * Fetch the length of the buffer from the first two bytes.
+ */
+		if (ppp->ubuf->head == ppp->ubuf->tail)
+			len = 0;
+		else {
+			GETC (c);
+			len = c << 8;
+			GETC (c);
+			len += c;
+		}
+/*
+ * If there is no length then wait for the data to arrive.
+ */
+		if (len == 0) {
+			/* no data */
+			clear_bit (0, &ppp->ubuf->locked);
+			if (file->f_flags & O_NONBLOCK) {
+				if (ppp->flags & SC_DEBUG)
+					printk (KERN_DEBUG
+						"ppp_tty_read: no data "
+						"(EAGAIN)\n");
+				return -EAGAIN;
+			}
+			current->timeout = 0;
+
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_DEBUG
+					"ppp_tty_read: sleeping(read_wait)\n");
+
+			interruptible_sleep_on (&ppp->read_wait);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			continue;
+		}
+/*
+ * Reset the time of the last read operation.
+ */
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_DEBUG "ppp_tty_read: len = %d\n", len);
+/*
+ * Ensure that the frame will fit within the caller's buffer. If not, then
+ * discard the frame from the input buffer and return an error to the caller.
+ */
+		if (len + 2 > nr) {
+			/* Can't copy it, update us_rbuff_head */
+
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_DEBUG
+				"ppp: read of %u bytes too small for %d "
+				"frame\n", nr, len + 2);
+			ppp->ubuf->tail += len;
+			ppp->ubuf->tail &= ppp->ubuf->size;
+			clear_bit (0, &ppp->ubuf->locked);
+			ppp->stats.ppp_ierrors++;
+			return -EOVERFLOW;
+		}
+/*
+ * Before we attempt to write the frame to the user, ensure that the
+ * page tables are proper.
+ */
+		indx = verify_area (VERIFY_WRITE, buf, len + 2);
+		if (indx != 0) {
+			ppp->ubuf->tail += len;
+			ppp->ubuf->tail &= ppp->ubuf->size;
+			clear_bit (0, &ppp->ubuf->locked);
+			return (indx);
+		}
+/*
+ * Fake the insertion of the ADDRESS and CONTROL information because these
+ * were not saved in the buffer.
+ */
+		put_byte_user (PPP_ALLSTATIONS, buf++);
+		put_byte_user (PPP_UI,          buf++);
+
+		indx = len;
+/*
+ * Copy the received data from the buffer to the caller's area.
+ */
+		while (indx-- > 0) {
+			GETC (c);
+			put_byte_user (c, buf);
+			++buf;
+		}
+/*
+ * Release the lock and return the character count in the buffer area.
+ */
+		clear_bit (0, &ppp->ubuf->locked);
+		len += 2; /* Account for ADDRESS and CONTROL bytes */
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_DEBUG
+				"ppp_tty_read: passing %d bytes up\n", len);
+		return len;
+	}
+#undef GETC
+}
+
+/* stuff a character into the transmit buffer, using PPP's way of escaping
+   special characters.
+   also, update fcs to take account of new character */
+
+extern inline void
+ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf,
+		register u_char chr)
+{
+/*
+ * The buffer should not be full.
+ */
+	if (ppp->flags & SC_DEBUG) {
+		if ((buf->count < 0) || (buf->count > 3000))
+			printk (KERN_DEBUG "ppp_stuff_char: %x %d\n",
+				(unsigned int) buf->count,
+				(unsigned int) chr);
+	}
+/*
+ * Update the FCS and if the character needs to be escaped, do it.
+ */
+	buf->fcs = PPP_FCS (buf->fcs, chr);
+	if (in_xmap (ppp, chr)) {
+		chr ^= PPP_TRANS;
+		ins_char (buf, PPP_ESCAPE);
+	}
+/*
+ * Add the character to the buffer.
+ */
+	ins_char (buf, chr);
+}
+
+/*
+ * Procedure to encode the data with the proper escapement and send the
+ * data to the remote system.
+ */
+
+static void
+ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
+		    u_char *data, int count, int non_ip)
+{
+	unsigned short int write_fcs;
+	int     address, control;
+	int	proto;
+/*
+ * Insert the leading FLAG character
+ */
+	buf->count = 0;
+
+	if (non_ip || flag_time == 0)
+		ins_char (buf, PPP_FLAG);
+	else {
+		if (jiffies - ppp->last_xmit > flag_time)
+			ins_char (buf, PPP_FLAG);
+	}
+	ppp->last_xmit = jiffies;
+	buf->fcs       = PPP_INITFCS;
+/*
+ * Emit the address/control information if needed
+ */
+	address = PPP_ADDRESS  (data);
+	control = PPP_CONTROL  (data);
+	proto   = PPP_PROTOCOL (data);
+
+	if (address != PPP_ALLSTATIONS ||
+	    control != PPP_UI ||
+	    (ppp->flags & SC_COMP_AC) == 0) {
+		ppp_stuff_char (ppp, buf, address);
+		ppp_stuff_char (ppp, buf, control);
+	}
+/*
+ * Emit the protocol (compressed if possible)
+ */
+	if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00))
+		ppp_stuff_char (ppp, buf, proto >> 8);
+
+	ppp_stuff_char (ppp, buf, proto);
+/*
+ * Insert the data
+ */
+	data  += 4;
+	count -= 4;
+
+	while (count-- > 0)
+		ppp_stuff_char (ppp, buf, *data++);
+/*
+ * Add the trailing CRC and the final flag character
+ */
+	write_fcs = buf->fcs ^ 0xFFFF;
+	ppp_stuff_char (ppp, buf, write_fcs);
+	ppp_stuff_char (ppp, buf, write_fcs >> 8);
+
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_DEBUG "ppp_dev_xmit_lower: fcs is %hx\n",
+			write_fcs);
+/*
+ * Add the trailing flag character
+ */
+	ins_char (buf, PPP_FLAG);
+/*
+ * Print the buffer
+ */
+	if (ppp->flags & SC_LOG_FLUSH)
+		ppp_print_buffer ("ppp flush", buf_base (buf),
+				  buf->count);
+	else {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_DEBUG
+				"ppp_dev_xmit: writing %d chars\n",
+				buf->count);
+	}
+/*
+ * Send the block to the tty driver.
+ */
+	ppp->stats.ppp_obytes += buf->count;
+	ppp_kick_tty (ppp, buf);
+}
+
+/*
+ * Send an frame to the remote with the proper bsd compression.
+ *
+ * Return 0 if frame was queued for transmission.
+ *        1 if frame must be re-queued for later driver support.
+ */
+
+static int
+ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf,
+		    u_char *data, int count)
+{
+	int	proto;
+	int     address, control;
+	u_char *new_data;
+	int     new_count;
+/*
+ * Print the buffer
+ */
+	if (ppp->flags & SC_LOG_OUTPKT)
+		ppp_print_buffer ("write frame", data, count);
+/*
+ * Determine if the frame may be compressed. Attempt to compress the
+ * frame if possible.
+ */
+	proto   = PPP_PROTOCOL (data);
+	address = PPP_ADDRESS  (data);
+	control = PPP_CONTROL  (data);
+
+	if (((ppp->flags & SC_COMP_RUN) != 0)	&&
+	    (ppp->sc_xc_state != (void *) 0)	&&
+	    (address == PPP_ALLSTATIONS)	&&
+	    (control == PPP_UI)			&&
+	    (proto != PPP_LCP)			&&
+	    (proto != PPP_CCP)) {
+		new_data = kmalloc (count, GFP_ATOMIC);
+		if (new_data == NULL) {
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_ERR
+					"ppp_dev_xmit_frame: no memory\n");
+			return 1;
+		}
+
+		new_count = bsd_compress (ppp->sc_xc_state,
+					  data,
+					  new_data,
+					  count,
+					  count);
+
+		if (new_count > 0) {
+			++ppp->stats.ppp_opackets;
+			ppp->stats.ppp_ooctects += new_count;
+
+			ppp_dev_xmit_lower (ppp, buf, new_data,
+					    new_count, 0);
+			kfree (new_data);
+			return 0;
+		}
+/*
+ * The frame could not be compressed.
+ */
+		kfree (new_data);
+	}
+/*
+ * The frame may not be compressed. Update the statistics before the
+ * count field is destroyed. The frame will be transmitted.
+ */
+	++ppp->stats.ppp_opackets;
+	ppp->stats.ppp_ooctects += count;
+/*
+ * Go to the escape encoding
+ */
+	ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00));
+	return 0;
+}
+
+/*
+ * Revise the tty frame for specific protocols.
+ */
+
+static int
+send_revise_frame (register struct ppp *ppp, u_char *data, int len)
+{
+	u_char *p;
+
+	switch (PPP_PROTOCOL (data)) {
+/*
+ * Update the LQR frame with the current MIB information. This saves having
+ * the daemon read old MIB data from the driver.
+ */
+	case PPP_LQR:
+		len = 48;			/* total size of this frame */
+		p   = (u_char *) &data [40];	/* Point to last two items. */
+		p   = store_long (p, ppp->stats.ppp_opackets + 1);
+		p   = store_long (p, ppp->stats.ppp_ooctects + len);
+		break;
+/*
+ * Outbound compression frames
+ */
+	case PPP_CCP:
+		ppp_proto_ccp (ppp,
+			       data + PPP_HARD_HDR_LEN,
+			       len  - PPP_HARD_HDR_LEN,
+			       0);
+		break;
+/*
+ * All other frame types
+ */
+	default:
+		break;
+	}
+
+	return len;
+}
+
+/*
+ * write a frame with NR chars from BUF to TTY
+ * we have to put the FCS field on ourselves
+ */
+
+static int
+ppp_tty_write (struct tty_struct *tty, struct file *file, const u_char * data,
+	       unsigned int count)
+{
+	struct ppp *ppp = tty2ppp (tty);
+	u_char *new_data;
+	int status;
+/*
+ * Verify the pointer to the PPP data and that the tty is still in PPP mode.
+ */
+	if (!ppp)
+		return -EIO;
+
+	if (ppp->magic != PPP_MAGIC)
+		return -EIO;
+
+	CHECK_PPP (-ENXIO);
+/*
+ * Ensure that the caller does not wish to send too much.
+ */
+	if (count > PPP_MTU) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_WARNING
+				"ppp_tty_write: truncating user packet "
+				"from %u to mtu %d\n", count, PPP_MTU);
+		count = PPP_MTU;
+	}
+/*
+ * Allocate a buffer for the data and fetch it from the user space.
+ */
+	new_data = kmalloc (count, GFP_KERNEL);
+	if (new_data == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+				"ppp_tty_write: no memory\n");
+		return 0;
+	}
+/*
+ * lock this PPP unit so we will be the only writer;
+ * sleep if necessary
+ */
+	while (lock_buffer (ppp->tbuf) != 0) {
+		current->timeout = 0;
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_DEBUG "ppp_tty_write: sleeping\n");
+		interruptible_sleep_on (&ppp->write_wait);
+
+		ppp = tty2ppp (tty);
+		if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) {
+			kfree (new_data);
+			return 0;
+		}
+
+		if (current->signal & ~current->blocked) {
+			kfree (new_data);
+			return -EINTR;
+		}
+	}
+/*
+ * Ensure that the caller's buffer is valid.
+ */
+	status = verify_area (VERIFY_READ, data, count);
+	if (status != 0) {
+		kfree (new_data);
+		ppp->tbuf->locked = 0;
+		return status;
+	}
+
+	memcpy_fromfs (new_data, data, count);
+/*
+ * Change the LQR frame
+ */
+	count = send_revise_frame (ppp, new_data, count);
+/*
+ * Send the data
+ */
+	ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
+	kfree (new_data);
+	return (int) count;
+}
+
+/*
+ * Process the BSD compression IOCTL event for the tty device.
+ */
+
+static int
+ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
+{
+	struct compressor *cp;
+	struct ppp_option_data data;
+	int error;
+	int nb;
+	u_char *ptr;
+	u_char ccp_option[CCP_MAX_OPTION_LENGTH];
+/*
+ * Fetch the compression parameters
+ */
+	error = verify_area (VERIFY_READ, odp, sizeof (data));
+	if (error == 0) {
+		memcpy_fromfs (&data, odp, sizeof (data));
+		nb  = data.length;
+		ptr = data.ptr;
+		if ((unsigned long) nb >= (unsigned long)CCP_MAX_OPTION_LENGTH)
+			nb = CCP_MAX_OPTION_LENGTH;
+	
+		error = verify_area (VERIFY_READ, ptr, nb);
+	}
+
+	if (error != 0)
+		return error;
+
+	memcpy_fromfs (ccp_option, ptr, nb);
+
+	if (ccp_option[1] < 2)	/* preliminary check on the length byte */
+		return (-EINVAL);
+
+	cp = find_compressor ((int) (unsigned) (unsigned char) ccp_option[0]);
+	if (cp != (struct compressor *) 0) {
+		/*
+		 * Found a handler for the protocol - try to allocate
+		 * a compressor or decompressor.
+		 */
+		error = 0;
+		if (data.transmit) {
+			if (ppp->sc_xc_state != NULL)
+				(*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state);
+
+			ppp->sc_xcomp    = cp;
+			ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
+
+			if (ppp->sc_xc_state == NULL) {
+				if (ppp->flags & SC_DEBUG)
+					printk("ppp%ld: comp_alloc failed\n",
+					       ppp2dev (ppp)->base_addr);
+				error = -ENOBUFS;
+			}
+			ppp->flags &= ~SC_COMP_RUN;
+		} else {
+			if (ppp->sc_rc_state != NULL)
+				(*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
+			ppp->sc_rcomp    = cp;
+			ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
+			if (ppp->sc_rc_state == NULL) {
+				if (ppp->flags & SC_DEBUG)
+					printk("ppp%ld: decomp_alloc failed\n",
+					       ppp2dev (ppp)->base_addr);
+				error = ENOBUFS;
+			}
+			ppp->flags &= ~SC_DECOMP_RUN;
+		}
+		return (error);
+	}
+
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "ppp%ld: no compressor for [%x %x %x], %x\n",
+		       ppp2dev (ppp)->base_addr, ccp_option[0], ccp_option[1],
+		       ccp_option[2], nb);
+	return (-EINVAL);	/* no handler found */
+}
+
+/*
+ * Process the IOCTL event for the tty device.
+ */
+
+static int
+ppp_tty_ioctl (struct tty_struct *tty, struct file *file, unsigned int param2,
+	       unsigned long param3)
+{
+	struct ppp *ppp = tty2ppp (tty);
+	register int temp_i = 0;
+	int error;
+/*
+ * Verify the status of the PPP device.
+ */
+	if (!ppp)
+		return -EBADF;
+
+	if (ppp->magic != PPP_MAGIC)
+		return -EBADF;
+
+	CHECK_PPP (-ENXIO);
+/*
+ * The user must have an euid of root to do these requests.
+ */
+	if (!suser ())
+		return -EPERM;
+/*
+ * Set the MRU value
+ */
+	switch (param2) {
+	case PPPIOCSMRU:
+		error = verify_area (VERIFY_READ, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			temp_i = get_int_user ((int *) param3);
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+				 "ppp_tty_ioctl: set mru to %x\n", temp_i);
+
+			if (ppp->mru != temp_i)
+				ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i);
+		}
+		break;
+/*
+ * Fetch the flags
+ */
+	case PPPIOCGFLAGS:
+		error = verify_area (VERIFY_WRITE, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			temp_i = (ppp->flags & SC_MASK);
+#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
+			temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 |
+				  SC_RCV_ODDP | SC_RCV_EVNP;
+#endif
+			put_long_user ((long) temp_i, param3);
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_DEBUG
+				"ppp_tty_ioctl: get flags: addr %lx flags "
+				"%x\n", param3, temp_i);
+		}
+		break;
+/*
+ * Set the flags for the various options
+ */
+	case PPPIOCSFLAGS:
+		error = verify_area (VERIFY_READ, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			temp_i  = get_int_user (param3) & SC_MASK;
+			temp_i |= (ppp->flags & ~SC_MASK);
+
+			if ((ppp->flags & SC_CCP_OPEN) &&
+			    (temp_i & SC_CCP_OPEN) == 0)
+				ppp_ccp_closed (ppp);
+
+			if ((ppp->flags | temp_i) & SC_DEBUG)
+				printk (KERN_INFO
+			       "ppp_tty_ioctl: set flags to %x\n", temp_i);
+			ppp->flags = temp_i;
+		}
+		break;
+/*
+ * Set the compression mode
+ */
+	case PPPIOCSCOMPRESS:
+		error = ppp_set_compression (ppp,
+					    (struct ppp_option_data *) param3);
+		break;
+/*
+ * Retrieve the transmit async map
+ */
+	case PPPIOCGASYNCMAP:
+		error = verify_area (VERIFY_WRITE, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			put_long_user (ppp->xmit_async_map[0], param3);
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+				     "ppp_tty_ioctl: get asyncmap: addr "
+				     "%lx asyncmap %lx\n",
+				     param3,
+				     (unsigned long) ppp->xmit_async_map[0]);
+		}
+		break;
+/*
+ * Set the transmit async map
+ */
+	case PPPIOCSASYNCMAP:
+		error = verify_area (VERIFY_READ, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			ppp->xmit_async_map[0] = get_long_user (param3);
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+				     "ppp_tty_ioctl: set xmit asyncmap %lx\n",
+				     (unsigned long) ppp->xmit_async_map[0]);
+		}
+		break;
+/*
+ * Set the receive async map
+ */
+	case PPPIOCSRASYNCMAP:
+		error = verify_area (VERIFY_READ, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			ppp->recv_async_map = get_long_user (param3);
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+				     "ppp_tty_ioctl: set rcv asyncmap %lx\n",
+				     (unsigned long) ppp->recv_async_map);
+		}
+		break;
+/*
+ * Obtain the unit number for this device.
+ */
+	case PPPIOCGUNIT:
+		error = verify_area (VERIFY_WRITE, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			put_long_user (ppp2dev (ppp)->base_addr, param3);
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+					"ppp_tty_ioctl: get unit: %ld",
+					ppp2dev (ppp)->base_addr);
+		}
+		break;
+/*
+ * Set the debug level
+ */
+	case PPPIOCSDEBUG:
+		error = verify_area (VERIFY_READ, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			temp_i  = (get_int_user (param3) & 0x1F) << 16;
+			temp_i |= (ppp->flags & ~0x1F0000);
+
+			if ((ppp->flags | temp_i) & SC_DEBUG)
+				printk (KERN_INFO
+			       "ppp_tty_ioctl: set flags to %x\n", temp_i);
+			ppp->flags = temp_i;
+		}
+		break;
+/*
+ * Get the debug level
+ */
+	case PPPIOCGDEBUG:
+		error = verify_area (VERIFY_WRITE, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			temp_i = (ppp->flags >> 16) & 0x1F;
+			put_long_user ((long) temp_i, param3);
+
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+					"ppp_tty_ioctl: get debug level %d\n",
+					temp_i);
+      		}
+		break;
+/*
+ * Get the times since the last send/receive frame operation
+ */
+	case PPPIOCGIDLE:
+		error = verify_area (VERIFY_WRITE, (void *) param3,
+				     sizeof (struct ppp_idle));
+		if (error == 0) {
+			struct ppp_idle cur_ddinfo;
+			unsigned long cur_jiffies = jiffies;
+
+			/* change absolute times to relative times. */
+			cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ;
+			cur_ddinfo.recv_idle = (cur_jiffies - ppp->ddinfo.recv_idle) / HZ;
+			memcpy_tofs ((void *) param3, &cur_ddinfo,
+				     sizeof (cur_ddinfo));
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+				"ppp_tty_ioctl: read demand dial info\n");
+		}
+		break;
+/*
+ * Retrieve the extended async map
+ */
+	case PPPIOCGXASYNCMAP:
+		error = verify_area (VERIFY_WRITE,
+				     (void *) param3,
+				     sizeof (ppp->xmit_async_map));
+		if (error == 0) {
+			memcpy_tofs ((void *) param3,
+				     ppp->xmit_async_map,
+				     sizeof (ppp->xmit_async_map));
+
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+				"ppp_tty_ioctl: get xasyncmap: addr %lx\n",
+				param3);
+		}
+		break;
+/*
+ * Set the async extended map
+ */
+	case PPPIOCSXASYNCMAP:
+		error = verify_area (VERIFY_READ, (void *) param3,
+				     sizeof (ppp->xmit_async_map));
+		if (error == 0) {
+			__u32 temp_tbl[8];
+
+			memcpy_fromfs (temp_tbl, (void *) param3,
+				       sizeof (ppp->xmit_async_map));
+			temp_tbl[1]  =  0x00000000;
+			temp_tbl[2] &= ~0x40000000;
+			temp_tbl[3] |=  0x60000000;
+
+			if ((temp_tbl[2] & temp_tbl[3]) != 0 ||
+			    (temp_tbl[4] & temp_tbl[5]) != 0 ||
+			    (temp_tbl[6] & temp_tbl[7]) != 0)
+				error = -EINVAL;
+			else {
+				memcpy (ppp->xmit_async_map, temp_tbl,
+					sizeof (ppp->xmit_async_map));
+
+				if (ppp->flags & SC_DEBUG)
+					printk (KERN_INFO
+					"ppp_tty_ioctl: set xasyncmap\n");
+			}
+		}
+		break;
+/*
+ * Set the maximum VJ header compression slot number.
+ */
+	case PPPIOCSMAXCID:
+		error = verify_area (VERIFY_READ, (void *) param3,
+				     sizeof (temp_i));
+		if (error == 0) {
+			temp_i = get_int_user (param3) + 1;
+			if (ppp->flags & SC_DEBUG)
+				printk (KERN_INFO
+				     "ppp_tty_ioctl: set maxcid to %d\n",
+				     temp_i);
+			if (ppp->slcomp != NULL)
+				slhc_free (ppp->slcomp);
+			ppp->slcomp = slhc_init (16, temp_i);
+
+			if (ppp->slcomp == NULL) {
+				if (ppp->flags & SC_DEBUG)
+					printk (KERN_ERR
+					"ppp: no space for compression buffers!\n");
+				ppp_release (ppp);
+				error = -ENOMEM;
+			}
+		}
+		break;
+/*
+ * Allow users to read, but not set, the serial port parameters
+ */
+	case TCGETS:
+	case TCGETA:
+		error = n_tty_ioctl (tty, file, param2, param3);
+		break;
+/*
+ *  All other ioctl() events will come here.
+ */
+	default:
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+				"ppp_tty_ioctl: invalid ioctl: %x, addr %lx\n",
+				param2,
+				param3);
+
+		error = -ENOIOCTLCMD;
+		break;
+	}
+	return error;
+}
+
+/*
+ * TTY callback.
+ *
+ * Process the select() statement for the PPP device.
+ */
+
+static int
+ppp_tty_select (struct tty_struct *tty, struct inode *inode,
+		struct file *filp, int sel_type, select_table * wait)
+{
+	struct ppp *ppp = tty2ppp (tty);
+	int result = 1;
+/*
+ * Verify the status of the PPP device.
+ */
+	if (!ppp)
+		return -EBADF;
+
+	if (ppp->magic != PPP_MAGIC)
+		return -EBADF;
+
+	CHECK_PPP (0);
+/*
+ * Branch on the type of select mode. A read request must lock the user
+ * buffer area.
+ */
+	switch (sel_type) {
+	case SEL_IN:
+		if (set_bit (0, &ppp->ubuf->locked) == 0) {
+			/* Test for the presence of data in the queue */
+			if (ppp->ubuf->head != ppp->ubuf->tail) {
+				clear_bit (0, &ppp->ubuf->locked);
+				break;
+			}
+			clear_bit (0, &ppp->ubuf->locked);
+		}		/* fall through */
+		/*
+ * Exceptions or read errors.
+ */
+	case SEL_EX:
+		/* Is this a pty link and the remote disconnected? */
+		if (tty->flags & (1 << TTY_SLAVE_CLOSED))
+			break;
+
+		/* Is this a local link and the modem disconnected? */
+		if (tty_hung_up_p (filp))
+			break;
+
+		select_wait (&ppp->read_wait, wait);
+		result = 0;
+		break;
+/*
+ * Write mode. A write is allowed if there is no current transmission.
+ */
+	case SEL_OUT:
+		if (ppp->tbuf->locked != 0) {
+			select_wait (&ppp->write_wait, wait);
+			result = 0;
+		}
+		break;
+	}
+	return result;
+}
+
+/*************************************************************
+ * NETWORK OUTPUT
+ *    This routine accepts requests from the network layer
+ *    and attempts to deliver the packets.
+ *    It also includes various routines we are compelled to
+ *    have to make the network layer work (arp, etc...).
+ *************************************************************/
+
+/*
+ * Callback from the network layer when the device goes up.
+ */
+
+static int
+ppp_dev_open (struct device *dev)
+{
+	struct ppp *ppp = dev2ppp (dev);
+
+	/* reset POINTOPOINT every time, since dev_close zaps it! */
+	dev->flags |= IFF_POINTOPOINT;
+
+	if (ppp2tty (ppp) == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+			"ppp: %s not connected to a TTY! can't go open!\n",
+			dev->name);
+		return -ENXIO;
+	}
+
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_INFO
+			"ppp: channel %s going up for IP packets!\n",
+			dev->name);
+
+	CHECK_PPP (-ENXIO);
+	return 0;
+}
+
+/*
+ * Callback from the network layer when the ppp device goes down.
+ */
+
+static int
+ppp_dev_close (struct device *dev)
+{
+	struct ppp *ppp = dev2ppp (dev);
+
+	if (ppp2tty (ppp) == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+			"ppp: %s not connected to a TTY! can't go down!\n",
+			dev->name);
+		return -ENXIO;
+	}
+/*
+ * We don't do anything about the device going down. It is not important
+ * for us.
+ */
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_INFO
+			"ppp: channel %s going down for IP packets!\n",
+			dev->name);
+	CHECK_PPP (-ENXIO);
+	return 0;
+}
+
+/*
+ * IOCTL operation to read the version of the driver.
+ */
+
+static int
+ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr)
+{
+        int error;
+	int len;
+	char *result;
+/*
+ * Must have write access to the buffer.
+ */
+	result = (char *) ifr->ifr_ifru.ifru_data;
+	len    = strlen (szVersion) + 1;
+	error  = verify_area (VERIFY_WRITE, result, len);
+/*
+ * Move the version data
+ */
+	if (error == 0)
+		memcpy_tofs (result, szVersion, len);
+
+	return error;
+}
+
+/*
+ * IOCTL to read the statistics for the pppstats program.
+ */
+
+static int
+ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
+{
+	struct ppp_stats *result, temp;
+	int    error;
+/*
+ * Must have write access to the buffer.
+ */
+	result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+	error = verify_area (VERIFY_WRITE,
+			     result,
+			     sizeof (temp));
+/*
+ * Supply the information for the caller. First move the version data
+ * then move the ppp stats; and finally the vj stats.
+ */
+	memset (&temp, 0, sizeof(temp));
+	if (error == 0 && dev->flags & IFF_UP) {
+		memcpy (&temp.p, &ppp->stats, sizeof (struct pppstat));
+		if (ppp->slcomp != NULL) {
+			temp.vj.vjs_packets    = ppp->slcomp->sls_o_compressed+
+						 ppp->slcomp->sls_o_uncompressed;
+			temp.vj.vjs_compressed = ppp->slcomp->sls_o_compressed;
+			temp.vj.vjs_searches   = ppp->slcomp->sls_o_searches;
+			temp.vj.vjs_misses     = ppp->slcomp->sls_o_misses;
+			temp.vj.vjs_errorin    = ppp->slcomp->sls_i_error;
+			temp.vj.vjs_tossed     = ppp->slcomp->sls_i_tossed;
+			temp.vj.vjs_uncompressedin = ppp->slcomp->sls_i_uncompressed;
+			temp.vj.vjs_compressedin   = ppp->slcomp->sls_i_compressed;
+		}
+	}
+/*
+ * Move the data to the caller's buffer
+ */
+	if (error == 0)
+		memcpy_tofs (result, &temp, sizeof (temp));
+	return error;
+}
+
+/*
+ * IOCTL to read the compression statistics for the pppstats program.
+ */
+
+static int
+ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
+{
+	struct ppp_comp_stats *result, temp;
+	int    error;
+/*
+ * Must have write access to the buffer.
+ */
+	result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data;
+	error = verify_area (VERIFY_WRITE,
+			     result,
+			     sizeof (temp));
+/*
+ * Supply the information for the caller.
+ */
+	memset (&temp, 0, sizeof(temp));
+	if (error == 0 && dev->flags & IFF_UP) {
+		if (ppp->sc_xc_state != NULL)
+			(*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state,
+						     &temp.c);
+
+		if (ppp->sc_rc_state != NULL)
+			(*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state,
+						       &temp.d);
+	}
+/*
+ * Move the data to the caller's buffer
+ */
+	if (error == 0)
+		memcpy_tofs (result, &temp, sizeof (temp));
+	return error;
+}
+
+/*
+ * Callback from the network layer to process the sockioctl functions.
+ */
+
+static int
+ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct ppp *ppp = dev2ppp (dev);
+	int error;
+/*
+ * Process the requests
+ */
+	switch (cmd) {
+	case SIOCGPPPSTATS:
+		error = ppp_dev_ioctl_stats (ppp, ifr, dev);
+		break;
+
+	case SIOCGPPPCSTATS:
+		error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev);
+		break;
+
+	case SIOCGPPPVER:
+		error = ppp_dev_ioctl_version (ppp, ifr);
+		break;
+
+	default:
+		error = -EINVAL;
+		break;
+	}
+	return error;
+}
+
+/*
+ * Send an IP frame to the remote with vj header compression.
+ *
+ * Return 0 if frame was queued for transmission.
+ *        1 if frame must be re-queued for later driver support.
+ */
+
+static int
+ppp_dev_xmit_ip1 (struct device *dev, struct ppp *ppp, u_char *data)
+{
+	int      proto = PPP_IP;
+	int	 len;
+	struct ppp_hdr    *hdr;
+	struct tty_struct *tty = ppp2tty (ppp);
+/*
+ * Obtain the length from the IP header.
+ */
+	len = ((struct iphdr *)data) -> tot_len;
+	len = ntohs (len);
+/*
+ * Validate the tty interface
+ */
+	if (tty == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+				"ppp_dev_xmit: %s not connected to a TTY!\n",
+				dev->name);
+		return 0;
+	}
+/*
+ * Ensure that the PPP device is still up
+ */
+	if (!(dev->flags & IFF_UP)) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_WARNING
+				"ppp_dev_xmit: packet sent on interface %s,"
+				" which is down for IP\n",
+				dev->name);
+		return 0;
+	}
+/*
+ * Detect a change in the transfer size
+ */
+	if (ppp->mtu != ppp2dev (ppp)->mtu) {
+		ppp_changedmtu (ppp,
+				ppp2dev (ppp)->mtu,
+				ppp->mru);
+	}
+/*
+ * Acquire the lock on the transmission buffer. If the buffer was busy then
+ * mark the device as busy and return "failure to send, try back later" error.
+ */
+	if (lock_buffer (ppp->wbuf) != 0) {
+		dev->tbusy = 1;
+		return 1;
+	}
+/*
+ * Print the frame being sent
+ */
+	if (ppp->flags & SC_LOG_OUTPKT)
+		ppp_print_buffer ("ppp outpkt", data, len);
+/*
+ * At this point, the buffer will be transmitted. There is no other exit.
+ *
+ * Try to compress the header.
+ */
+	if (ppp->flags & SC_COMP_TCP) {
+		len = slhc_compress (ppp->slcomp, data, len,
+				     buf_base (ppp->cbuf) + PPP_HARD_HDR_LEN,
+				     &data,
+				     (ppp->flags & SC_NO_TCP_CCID) == 0);
+
+		if (data[0] & SL_TYPE_COMPRESSED_TCP) {
+			proto    = PPP_VJC_COMP;
+			data[0] ^= SL_TYPE_COMPRESSED_TCP;
+		} else {
+			if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
+				proto = PPP_VJC_UNCOMP;
+			data[0] = (data[0] & 0x0f) | 0x40;
+		}
+	}
+/*
+ * Send the frame
+ */
+	len  += PPP_HARD_HDR_LEN;
+	hdr   = &((struct ppp_hdr *) data)[-1];
+
+	hdr->address     = PPP_ALLSTATIONS;
+	hdr->control     = PPP_UI;
+	hdr->protocol[0] = 0;
+	hdr->protocol[1] = proto;
+
+	return ppp_dev_xmit_frame (ppp, ppp->wbuf, (u_char *) hdr, len);
+}
+
+/*
+ * This is just an interum solution until the 1.3 kernel's networking is
+ * available. The 1.2 kernel has problems with device headers before the
+ * buffers.
+ *
+ * This routine should be deleted, and the ppp_dev_xmit_ip1 routine called
+ * by this name.
+ */
+
+static int
+ppp_dev_xmit_ip (struct device *dev, struct ppp *ppp, u_char *data)
+{
+	struct ppp_hdr *hdr;
+	int     len;
+	int     answer;
+
+	len = ((struct iphdr *)data) -> tot_len;
+	len = ntohs (len);
+
+	hdr = (struct ppp_hdr *) kmalloc (len + sizeof (struct ppp_hdr),
+					  GFP_ATOMIC);
+
+	if (hdr == NULL)
+		answer = 1;
+	else {
+		memcpy (&hdr[1], data, len);
+		answer = ppp_dev_xmit_ip1 (dev, ppp, (u_char *) &hdr[1]);
+		kfree (hdr);
+	}
+
+	return answer;
+}
+
+/*
+ * Send an IPX (or any other non-IP) frame to the remote.
+ *
+ * Return 0 if frame was queued for transmission.
+ *        1 if frame must be re-queued for later driver support.
+ */
+
+#ifdef NEW_SKBUFF
+static int
+ppp_dev_xmit_ipx1 (struct device *dev, struct ppp *ppp,
+		  u_char *data, int len, int proto)
+{
+	struct tty_struct *tty = ppp2tty (ppp);
+	struct ppp_hdr    *hdr;
+/*
+ * Validate the tty interface
+ */
+	if (tty == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+				"ppp_dev_xmit: %s not connected to a TTY!\n",
+				dev->name);
+		return 0;
+	}
+/*
+ * Ensure that the PPP device is still up
+ */
+	if (!(dev->flags & IFF_UP)) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_WARNING
+				"ppp_dev_xmit: packet sent on interface %s,"
+				" which is down\n",
+				dev->name);
+		return 0;
+	}
+/*
+ * Detect a change in the transfer size
+ */
+	if (ppp->mtu != ppp2dev (ppp)->mtu) {
+		ppp_changedmtu (ppp,
+				ppp2dev (ppp)->mtu,
+				ppp->mru);
+	}
+/*
+ * Acquire the lock on the transmission buffer. If the buffer was busy then
+ * mark the device as busy and return "failure to send, try back later" error.
+ */
+	if (lock_buffer (ppp->wbuf) != 0) {
+		dev->tbusy = 1;
+		return 1;
+	}
+/*
+ * Print the frame being sent
+ */
+	if (ppp->flags & SC_LOG_OUTPKT)
+		ppp_print_buffer ("ppp outpkt", data, len);
+/*
+ * Send the frame
+ */
+	len  += PPP_HARD_HDR_LEN;
+	hdr   = &((struct ppp_hdr *) data)[-1];
+
+	hdr->address     = PPP_ALLSTATIONS;
+	hdr->control     = PPP_UI;
+	hdr->protocol[0] = proto >> 8;
+	hdr->protocol[1] = proto;
+
+	return ppp_dev_xmit_frame (ppp, ppp->wbuf, (u_char *) hdr, len);
+}
+
+/*
+ * This is just an interum solution until the 1.3 kernel's networking is
+ * available. The 1.2 kernel has problems with device headers before the
+ * buffers.
+ *
+ * This routine should be deleted, and the ppp_dev_xmit_ipx1 routine called
+ * by this name.
+ */
+
+static int
+ppp_dev_xmit_ipx (struct device *dev, struct ppp *ppp,
+		  u_char *data, int len, int proto)
+{
+	struct ppp_hdr    *hdr;
+	int     answer;
+
+	hdr = (struct ppp_hdr *) kmalloc (len + sizeof (struct ppp_hdr),
+					  GFP_ATOMIC);
+	if (hdr == NULL)
+		answer = 1;
+	else {
+		memcpy (&hdr[1], data, len);
+		answer = ppp_dev_xmit_ipx1 (dev, ppp, (u_char *) &hdr[1],
+					    len, proto);
+		kfree (hdr);
+	}
+
+	return answer;
+}
+#endif
+
+/*
+ * Send a frame to the remote.
+ */
+
+static int
+ppp_dev_xmit (sk_buff *skb, struct device *dev)
+{
+	int answer, len;
+	u_char *data;
+	struct ppp        *ppp = dev2ppp (dev);
+	struct tty_struct *tty = ppp2tty (ppp);
+/*
+ * just a little sanity check.
+ */
+	if (skb == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_WARNING "ppp_dev_xmit: null packet!\n");
+		return 0;
+	}
+/*
+ * Avoid timing problem should tty hangup while data is queued to be sent
+ */
+	if (!ppp->inuse) {
+		dev_kfree_skb (skb, FREE_WRITE);
+		dev_close (dev);
+		return 0;
+	}
+/*
+ * Validate the tty linkage
+ */
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_DEBUG "ppp_dev_xmit [%s]: skb %p\n",
+			dev->name, skb);
+/*
+ * Validate the tty interface
+ */
+	if (tty == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk (KERN_ERR
+				"ppp_dev_xmit: %s not connected to a TTY!\n",
+				dev->name);
+		dev_kfree_skb (skb, FREE_WRITE);
+		return 0;
+	}
+/*
+ * Fetch the pointer to the data
+ */
+	len   = skb->len;
+	data  = skb_data(skb);
+/*
+ * Look at the protocol in the skb to determine the difference between
+ * an IP frame and an IPX frame.
+ */
+
+#ifdef NEW_SKBUFF
+	switch (skb->protocol) {
+	case htons (ETH_P_IPX):
+		answer = ppp_dev_xmit_ipx (dev, ppp, data, len, PPP_IPX);
+		break;
+
+	case htons (ETH_P_IP):
+		answer = ppp_dev_xmit_ip (dev, ppp, data);
+		break;
+
+	default: /* All others have no support at this time. */
+		dev_kfree_skb (skb, FREE_WRITE);
+		return 0;
+	}
+#else
+	answer = ppp_dev_xmit_ip (dev, ppp, data);
+#endif
+
+/*
+ * This is the end of the transmission. Release the buffer if it was sent.
+ */
+	if (answer == 0) {
+		dev_kfree_skb (skb, FREE_WRITE);
+		ppp->ddinfo.xmit_idle = jiffies;
+	}
+	return answer;
+}
+
+/*
+ * Generate the statistic information for the /proc/net/dev listing.
+ */
+
+static struct enet_statistics *
+ppp_dev_stats (struct device *dev)
+{
+	struct ppp *ppp = dev2ppp (dev);
+	static struct enet_statistics ppp_stats;
+
+	ppp_stats.rx_packets          = ppp->stats.ppp_ipackets;
+	ppp_stats.rx_errors           = ppp->stats.ppp_ierrors;
+	ppp_stats.rx_dropped          = ppp->stats.ppp_ierrors;
+	ppp_stats.rx_fifo_errors      = 0;
+	ppp_stats.rx_length_errors    = 0;
+	ppp_stats.rx_over_errors      = 0;
+	ppp_stats.rx_crc_errors       = 0;
+	ppp_stats.rx_frame_errors     = 0;
+	ppp_stats.tx_packets          = ppp->stats.ppp_opackets;
+	ppp_stats.tx_errors           = ppp->stats.ppp_oerrors;
+	ppp_stats.tx_dropped          = 0;
+	ppp_stats.tx_fifo_errors      = 0;
+	ppp_stats.collisions          = 0;
+	ppp_stats.tx_carrier_errors   = 0;
+	ppp_stats.tx_aborted_errors   = 0;
+	ppp_stats.tx_window_errors    = 0;
+	ppp_stats.tx_heartbeat_errors = 0;
+
+	if (ppp->flags & SC_DEBUG)
+		printk (KERN_INFO "ppp_dev_stats called");
+	return &ppp_stats;
+}
+
+#ifdef NEW_SKBUFF
+/*
+ *	The PPP protocol is currently pure IP (no IPX yet). This defines
+ *      the protocol layer which is blank since the driver does all the
+ *      cooking.
+ */
+
+static int ppp_dev_input (struct protocol *self, struct protocol *lower,
+			  sk_buff *skb, void *saddr, void *daddr)
+{
+	return protocol_pass_demultiplex(self, NULL, skb, NULL, NULL);
+}
+
+static int ppp_dev_output (struct protocol *self, sk_buff *skb, int type,
+			   int subid, void *saddr, void *daddr, void *opt)
+{
+	if(skb->dev==NULL)
+	{
+		printk("ppp_dev_output: No device.\n");
+		kfree_skb(skb, FREE_WRITE);
+		return -1;
+	}
+	dev_queue_xmit(skb, skb->dev, skb->priority);
+	return 0;
+}
+
+static int ppp_dev_getkey(int protocol, int subid, unsigned char *key)
+{
+	switch (protocol)
+	{
+	case htons (ETH_P_IP):
+	case htons (ETH_P_IPX):
+		return 0;
+
+	default:
+		break;
+	}
+
+	return -EAFNOSUPPORT;
+}
+
+#else
+
+#if USE_SKB_PROTOCOL == 0
+/*
+ * Called to enquire about the type of the frame in the buffer. Return
+ * ETH_P_IP for an IP frame, ETH_P_IPX for an IPX frame.
+ */
+
+static unsigned short
+ppp_dev_type (sk_buff *skb, struct device *dev)
+{
+	return (htons (ETH_P_IP));
+}
+#endif
+
+#if USE_SKB_PROTOCOL == 0
+static int ppp_dev_header (unsigned char *buff, struct device *dev,
+			   unsigned short type, void *daddr, void *saddr,
+			   unsigned len, struct sk_buff *skb)
+#else
+static int ppp_dev_header (sk_buff *skb, struct device *dev,
+			   unsigned short type, void *daddr,
+			   void *saddr, unsigned len)
+#endif
+{
+	return (0);
+}
+
+static int
+ppp_dev_rebuild (void *buff, struct device *dev, unsigned long raddr,
+		 sk_buff *skb)
+{
+	return (0);
+}
+#endif
+
+/*************************************************************
+ * UTILITIES
+ *    Miscellany called by various functions above.
+ *************************************************************/
+
+/* allocate or create a PPP channel */
+static struct ppp *
+ppp_alloc (void)
+{
+	int		if_num;
+	int		status;
+	ppp_ctrl_t	*ctl;
+	struct device	*dev;
+	struct ppp	*ppp;
+
+	/* try to find an free device */
+	ctl      = ppp_list;
+	if_num   = 0;
+  
+	while (ctl) {
+		ppp = ctl2ppp (ctl);
+		if (!set_bit(0, &ppp->inuse))
+			return (ppp);
+		ctl = ctl->next;
+		if (++if_num == max_dev)
+			return (NULL);
+	}
+/*
+ * There are no available items. Allocate a device from the system pool
+ */
+	ctl = (ppp_ctrl_t *) kmalloc (sizeof(ppp_ctrl_t), GFP_KERNEL);
+	if (ctl) {
+		(void) memset(ctl, 0, sizeof(ppp_ctrl_t));
+		ppp = ctl2ppp (ctl);
+		dev = ctl2dev (ctl);
+
+		/* initialize channel control data */
+		set_bit(0, &ppp->inuse);
+
+		ppp->line      = if_num;
+		ppp->tty       = NULL;
+		ppp->dev       = dev;
+    
+		dev->next      = NULL;
+		dev->init      = ppp_init_dev;
+		dev->name      = ctl->name;
+		dev->base_addr = (unsigned long) if_num;
+		dev->priv      = (void *) ppp;
+
+		sprintf (dev->name, "ppp%d", if_num);
+    
+		/* link in the new channel */
+		ctl->next      = ppp_list;
+		ppp_list       = ctl;
+
+/* register device so that we can be ifconfig'd */
+/* ppp_init_dev() will be called as a side-effect */
+
+		status = register_netdev (dev);
+		if (status == 0) {
+			printk (KERN_INFO "registered device %s\n", dev->name);
+			return (ppp);
+		}
+
+		printk (KERN_ERR
+		       "ppp_alloc - register_netdev(%s) = %d failure.\n",
+			dev->name, status);
+		/* This one will forever be busy as it is not initialized */
+	}
+	return (NULL);
+}
+
+/*
+ * Utility procedures to print a buffer in hex/ascii
+ */
+
+static void
+ppp_print_hex (register u_char * out, const u_char * in, int count)
+{
+	register u_char next_ch;
+	static char hex[] = "0123456789ABCDEF";
+
+	while (count-- > 0) {
+		next_ch = *in++;
+		*out++ = hex[(next_ch >> 4) & 0x0F];
+		*out++ = hex[next_ch & 0x0F];
+		++out;
+	}
+}
+
+static void
+ppp_print_char (register u_char * out, const u_char * in, int count)
+{
+	register u_char next_ch;
+
+	while (count-- > 0) {
+		next_ch = *in++;
+
+		if (next_ch < 0x20 || next_ch > 0x7e)
+			*out++ = '.';
+		else {
+			*out++ = next_ch;
+			if (next_ch == '%')   /* printk/syslogd has a bug !! */
+				*out++ = '%';
+		}
+	}
+	*out = '\0';
+}
+
+static void
+ppp_print_buffer (const u_char * name, const u_char * buf, int count)
+{
+	u_char line[44];
+
+	if (name != (u_char *) NULL)
+		printk (KERN_DEBUG "ppp: %s, count = %d\n", name, count);
+
+	while (count > 8) {
+		memset (line, 32, 44);
+		ppp_print_hex (line, buf, 8);
+		ppp_print_char (&line[8 * 3], buf, 8);
+		printk (KERN_DEBUG "%s\n", line);
+		count -= 8;
+		buf += 8;
+	}
+
+	if (count > 0) {
+		memset (line, 32, 44);
+		ppp_print_hex (line, buf, count);
+		ppp_print_char (&line[8 * 3], buf, count);
+		printk (KERN_DEBUG "%s\n", line);
+	}
+}
+
+/*************************************************************
+ * Compressor module interface
+ *************************************************************/
+
+struct compressor_link {
+	struct compressor_link	*next;
+	struct compressor	*comp;
+};
+
+static struct compressor_link *ppp_compressors = (struct compressor_link *) 0;
+
+static struct compressor *find_compressor (int type)
+{
+	struct compressor_link *lnk;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	lnk = ppp_compressors;
+	while (lnk != (struct compressor_link *) 0) {
+		if ((int) (unsigned char) lnk->comp->compress_proto == type) {
+			restore_flags(flags);
+			return lnk->comp;
+		}
+		lnk = lnk->next;
+	}
+
+	restore_flags(flags);
+	return (struct compressor *) 0;
+}
+
+static int ppp_register_compressor (struct compressor *cp)
+{
+	struct compressor_link *new;
+	unsigned long flags;
+
+	new = (struct compressor_link *) kmalloc (sizeof (struct compressor_link), GFP_KERNEL);
+
+	if (new == (struct compressor_link *) 0)
+	  	return 1;
+
+	save_flags(flags);
+	cli();
+
+	if (find_compressor (cp->compress_proto)) {
+		restore_flags(flags);
+	  	kfree (new);
+		return 0;
+	}
+
+	new->next	= ppp_compressors;
+	new->comp	= cp;
+	ppp_compressors = new;
+
+	restore_flags(flags);
+	return 0;
+}
+
+static void ppp_unregister_compressor (struct compressor *cp)
+{
+	struct compressor_link *prev = (struct compressor_link *) 0;
+	struct compressor_link *lnk;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	lnk  = ppp_compressors;
+	while (lnk != (struct compressor_link *) 0) {
+		if (lnk->comp == cp) {
+		  	if (prev)
+				prev->next = lnk->next;
+			else
+				ppp_compressors = lnk->next;
+			kfree (lnk);
+			break;
+		}
+		prev = lnk;
+		lnk  = lnk->next;
+	}
+	restore_flags(flags);
+}
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+	int status;
+
+	/* register our line disciplines */
+	status = ppp_first_time();
+	if (status != 0)
+		printk (KERN_INFO
+		       "PPP: ppp_init() failure %d\n", status);
+	else
+		(void) register_symtab (&ppp_syms);
+	return (status);
+}
+
+void
+cleanup_module(void)
+{
+	int status;
+	ppp_ctrl_t *ctl, *next_ctl;
+	struct device *dev;
+	struct ppp *ppp;
+	int busy_flag = 0;
+/*
+ * Ensure that the devices are not in operation.
+ */
+	ctl = ppp_list;
+	while (ctl) {
+		ppp = ctl2ppp (ctl);
+		if (ppp->inuse && ppp->tty != NULL) {
+			busy_flag = 1;
+			break;
+		}
+
+		dev = ctl2dev (ctl);
+		if (dev->start || dev->flags & IFF_UP) {
+			busy_flag = 1;
+			break;
+		}
+		ctl = ctl->next;
+	}
+/*
+ * Ensure that there are no compressor modules registered
+ */
+	if (busy_flag == 0 && ppp_compressors != NULL)
+		busy_flag = 1;
+
+	if (busy_flag) {
+		printk (KERN_INFO
+			"PPP: device busy, remove delayed\n");
+		return;
+	}
+/*
+ * Release the tty registration of the line dicipline so that no new entries
+ * may be created.
+ */
+	status = tty_register_ldisc (N_PPP, NULL);
+	if (status != 0)
+		printk (KERN_INFO
+			"PPP: Unable to unregister ppp line discipline "
+			"(err = %d)\n", status);
+	else
+		printk (KERN_INFO
+		       "PPP: ppp line discipline successfully unregistered\n");
+/*
+ * De-register the devices so that there is no problem with them
+ */	
+	next_ctl = ppp_list;
+	while (next_ctl) {
+		ctl      = next_ctl;
+		next_ctl = ctl->next;
+		ppp      = ctl2ppp (ctl);
+		dev      = ctl2dev (ctl);
+
+		ppp_release       (ppp);
+		unregister_netdev (dev);
+		kfree (ctl);
+	}
+}
+#endif
diff -urNwbB linux/arch/arm/drivers/scsi/Makefile linux.arm/arch/arm/drivers/scsi/Makefile
--- linux/arch/arm/drivers/scsi/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/Makefile	Sat Feb 24 13:17:59 1996
@@ -0,0 +1,147 @@
+
+# Makefile for kernel/blk_drv/scsi
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+all: links first_rule
+
+L_TARGET := scsi.a
+L_OBJS   :=
+M_OBJS   :=
+MOD_LIST_NAME := SCSI_MODULES
+SCSI_SRCS := $(wildcard $(L_OBJS:%.o=%.c))
+
+ifeq (${CFLAGS},)
+CFLAGS = -D__KERNEL__ \
+	-Wall -Wstrict-prototypes -I. -I../../../../include \
+	-O2 -fomit-frame-pointer
+
+include ../../../../.config
+
+TOPDIR = ../../../..
+endif
+
+ifeq ($(CONFIG_SCSI),y)
+L_OBJS += hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
+else
+  ifeq ($(CONFIG_SCSI),m)
+    ifdef CONFIG_MODVERSIONS
+      # Create this before we build anything else.
+      SCSI_MODULE_VER := scsi_syms.ver
+      SYMTAB_OBJS := scsi_syms.o
+    endif
+    M_OBJS += scsi_mod.o
+  endif
+endif
+
+ifeq ($(CONFIG_CHR_DEV_ST),y)
+L_OBJS += st.o
+else
+  ifeq ($(CONFIG_CHR_DEV_ST),m)
+  M_OBJS += st.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SD),y)
+L_OBJS += sd.o sd_ioctl.o
+else
+  ifeq ($(CONFIG_BLK_DEV_SD),m)
+  M_OBJS += sd_mod.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_SR),y)
+L_OBJS += sr.o sr_ioctl.o
+else
+  ifeq ($(CONFIG_BLK_DEV_SR),m)
+  M_OBJS += sr_mod.o
+  endif
+endif
+
+ifeq ($(CONFIG_CHR_DEV_SG),y)
+L_OBJS += sg.o
+else
+  ifeq ($(CONFIG_CHR_DEV_SG),m)
+  M_OBJS += sg.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_CUMANA_1),y)
+L_OBJS += cumana_1.o
+else
+  ifeq ($(CONFIG_SCSI_CUMANA_1),m)
+  M_OBJS += cumana_1.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_ECOSCSI),y)
+L_OBJS += ecoscsi.o
+else
+  ifeq ($(CONFIG_SCSI_ECOSCSI),m)
+  M_OBJS += ecoscsi.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_OAK1),y)
+L_OBJS += oak.o
+else
+  ifeq ($(CONFIG_SCSI_OAK1),m)
+  M_OBJS += oak_mod.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_ACORNSCSI_3),y)
+L_OBJS += acornscsi.o acornscsi-io.o
+else
+  ifeq ($(CONFIG_SCSI_ACORNSCSI_3),m)
+  M_OBJS += acornscsi_mod.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+ifeq ($(CONFIG_SCSI),m)
+  ifdef CONFIG_MODVERSIONS
+	include ../../../../versions.mk
+  endif
+endif
+
+fastdep: links
+
+scsi_mod.o: $(SCSI_MODULE_VER) hosts.o scsi.o scsi_ioctl.o constants.o \
+		scsicam.o scsi_proc.o $(SYMTAB_OBJS)
+	$(LD) $(LD_RFLAG) -r -o $@ hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o $(SYMTAB_OBJS)
+
+sr_mod.o: $(SCSI_MODULE_VER) sr.o sr_ioctl.o
+	$(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o
+
+sd_mod.o: $(SCSI_MODULE_VER) sd.o sd_ioctl.o
+	$(LD) $(LD_RFLAG) -r -o $@ sd.o sd_ioctl.o
+
+oak_mod.o: oak.o
+	$(LD) $(LD_RFLAG) -r -o oak_mod.o oak.o `gcc --print-libgcc-file-name`
+
+acornscsi_mod.o: acornscsi.o acornscsi-io.o
+	$(LD) $(LD_RFLAG) -r -o acornscsi_mod.o acornscsi.o acornscsi-io.o `gcc --print-libgcc-file-name`
+
+$(SYMTAB_OBJS): $(SYMTAB_OBJS:.o=.c)
+
+modules: $(SCSI_MODULE_VER)
+
+LK = constants.c constants.h hosts.h scsi.h scsi_ioctl.c scsi_ioctl.h scsi_proc.c scsi_syms.c \
+	scsicam.c sd.c sd.h sd_ioctl.c sg.c sg.h sr.c sr.h sr_ioctl.c st.c st.h st_options.h
+
+links:
+	-@for f in $(LK); do \
+		ln -s ../../../../drivers/scsi/$$f .; \
+	done
+	touch links
+
+LINKCLEAN:
+	-@for f in $(LK); do \
+		if [ -L $$f ]; then rm -f $$f; fi; \
+	done
+	rm -f links
diff -urNwbB linux/arch/arm/drivers/scsi/NCR5380.c linux.arm/arch/arm/drivers/scsi/NCR5380.c
--- linux/arch/arm/drivers/scsi/NCR5380.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/NCR5380.c	Sat Feb 24 09:36:54 1996
@@ -0,0 +1,2786 @@
+#define NDEBUG (NDEBUG_RESTART_SELECT)
+/* 
+ * NCR 5380 generic driver routines.  These should make it *trivial*
+ * 	to implement 5380 SCSI drivers under Linux with a non-trantor
+ *	architecture.
+ *
+ *	Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing 
+ *	(Unix and Linux consulting and custom programming)
+ * 	drew@colorado.edu
+ *	+1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6. 
+ *
+ * For more information, please consult 
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.c,v $
+ * Revision 1.5  1994/01/19  09:14:57  drew
+ * Fixed udelay() hack that was being used on DATAOUT phases
+ * instead of a proper wait for the final handshake.
+ *
+ * Revision 1.4  1994/01/19  06:44:25  drew
+ * *** empty log message ***
+ *
+ * Revision 1.3  1994/01/19  05:24:40  drew
+ * Added support for TCR LAST_BYTE_SENT bit.
+ *
+ * Revision 1.2  1994/01/15  06:14:11  drew
+ * REAL DMA support, bug fixes.
+ *
+ * Revision 1.1  1994/01/15  06:00:54  drew
+ * Initial revision
+ *
+ */
+
+/*
+ * Further development / testing that should be done : 
+ * 1.  Cleanup the NCR5380_transfer_dma function and DMA operation complete
+ *     code so that everything does the same thing that's done at the 
+ *     end of a pseudo-DMA read operation.
+ *
+ * 2.  Fix REAL_DMA (interrupt driven, polled works fine) -
+ *     basically, transfer size needs to be reduced by one 
+ *     and the last byte read as is done with PSEUDO_DMA.
+ * 
+ * 3.  Test USLEEP code 
+ *
+ * 4.  Test SCSI-II tagged queueing (I have no devices which support 
+ *	tagged queueing)
+ *
+ * 5.  Test linked command handling code after Eric is ready with 
+ *      the high level code.
+ */
+
+#ifndef notyet
+#undef LINKED
+#undef USLEEP
+#undef REAL_DMA
+#endif
+
+#ifdef REAL_DMA_POLL
+#undef READ_OVERRUNS
+#define READ_OVERRUNS
+#endif
+
+/*
+ * Design
+ * Issues :
+ *
+ * The other Linux SCSI drivers were written when Linux was Intel PC-only,
+ * and specifically for each board rather than each chip.  This makes their
+ * adaptation to platforms like the Mac (Some of which use NCR5380's)
+ * more difficult than it has to be.
+ *
+ * Also, many of the SCSI drivers were written before the command queuing
+ * routines were implemented, meaning their implementations of queued 
+ * commands were hacked on rather than designed in from the start.
+ *
+ * When I designed the Linux SCSI drivers I figured that 
+ * while having two different SCSI boards in a system might be useful
+ * for debugging things, two of the same type wouldn't be used.
+ * Well, I was wrong and a number of users have mailed me about running
+ * multiple high-performance SCSI boards in a server.
+ *
+ * Finally, when I get questions from users, I have no idea what 
+ * revision of my driver they are running.
+ *
+ * This driver attempts to address these problems :
+ * This is a generic 5380 driver.  To use it on a different platform, 
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use 
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * As far as command queueing, two queues are maintained for 
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing.  This means that an 
+ * unlimited number of commands may be queued, letting 
+ * more commands propagate from the higher driver levels giving higher 
+ * throughput.  Note that both I_T_L and I_T_L_Q nexuses are supported, 
+ * allowing multiple commands to propagate all the way to a SCSI-II device 
+ * while a command is already executing.
+ *
+ * To solve the multiple-boards-in-the-same-system problem, 
+ * there is a separate instance structure for each instance
+ * of a 5380 in the system.  So, multiple NCR5380 drivers will
+ * be able to coexist with appropriate changes to the high level
+ * SCSI code.  
+ *
+ * A NCR5380_PUBLIC_REVISION macro is provided, with the release
+ * number (updated for each public release) printed by the 
+ * NCR5380_print_options command, which should be called from the 
+ * wrapper detect function, so that I know what release of the driver
+ * users are using.
+ *
+ * Issues specific to the NCR5380 : 
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead 
+ * piece of hardware that requires you to sit in a loop polling for 
+ * the REQ signal as long as you are connected.  Some devices are 
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect 
+ * while doing long seek operations.
+ * 
+ * The workaround for this is to keep track of devices that have
+ * disconnected.  If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like 
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ * 
+ * Some tweaking of N and M needs to be done.  An algorithm based 
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these 
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started when not running by the interrupt handler,
+ * timer, and queue command function.  It attempts to establish
+ * I_T_L or I_T_L_Q nexuses by removing the commands from the 
+ * issue queue and calling NCR5380_select() if a nexus 
+ * is not established. 
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work.  If USLEEP
+ * was defined, and the target is idle for too long, the system
+ * will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr()  which will in turn call NCR5380_reselect
+ * to reestablish a nexus.  This will run main if necessary.
+ *
+ * On command termination, the done function will be called as 
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command 
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips.  To use it, you write a architecture specific functions 
+ * and macros and include this file in your driver.
+ *
+ * These macros control options : 
+ * AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be 
+ *	defined.
+ * 
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ *	for commands that return with a CHECK CONDITION status. 
+ *
+ * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
+ * 	transceivers. 
+ *
+ * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
+ *	bytes at a time.  Since interrupts are disabled by default during
+ *	these transfers, we might need this to give reasonable interrupt
+ *	service time if the transfer size gets too large.
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * REAL_DMA_POLL - if defined, REAL DMA is used but the driver doesn't
+ * 	rely on phase mismatch and EOP interrupts to determine end 
+ *	of phase.
+ *
+ * SCSI2 - if defined, SCSI-2 tagged queuing is used where possible
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers.  You
+ *	    only really want to use this if you're having a problem with
+ *	    dropped characters during high speed communications, and even
+ *	    then, you're going to be better off twiddling with transfersize
+ *	    in the high level code.
+ *
+ * USLEEP - if defined, on devices that aren't disconnecting from the 
+ *	bus, we will go to sleep so that the CPU can get real work done 
+ *	when we run a command that won't complete immediately.
+ *
+ * Note that if USLEEP is defined, NCR5380_TIMER *must* also be
+ * defined.
+ *
+ * Defaults for these will be provided if USLEEP is defined, although
+ * the user may want to adjust these to allocate CPU resources to 
+ * the SCSI driver or "real" code.
+ * 
+ * USLEEP_SLEEP - amount of time, in jiffies, to sleep
+ *
+ * USLEEP_POLL - amount of time, in jiffies, to poll
+ *
+ * These macros MUST be defined :
+ * NCR5380_local_declare() - declare any local variables needed for your transfer
+ *	routines.
+ *
+ * NCR5380_setup(instance) - initialize any local variables needed from a given
+ *	instance of the host adapter for NCR5380_{read,write,pread,pwrite}
+ * 
+ * NCR5380_read(register)  - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register 
+ *
+ * NCR5380_implementation_fields  - additional fields needed for this 
+ *	specific implementation of the NCR5380
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions : 
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes 
+ *	that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are 
+ *	available as NCR5380_i386_dma_write_setup,
+ *	NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * If nothing specific to this implementation needs doing (ie, with external
+ * hardware), you must also define 
+ *  
+ * NCR5380_queue_command
+ * NCR5380_reset
+ * NCR5380_abort
+ *
+ * to be the global entry points into the specific driver, ie 
+ * #define NCR5380_queue_command t128_queue_command.
+ *
+ * If this is not done, the routines will be defined as static functions
+ * with the NCR5380* names and the user must provide a globally
+ * accessible wrapper function.
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID.  If the 
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used.  Before the specific driver initialization
+ * code finishes, NCR5380_print_options should be called.
+ */
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/*
+ * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+ *
+ * Purpose : initialize the saved data pointers for cmd to point to the 
+ *	start of the buffer.
+ *
+ * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) {
+    /* 
+     * Initialize the Scsi Pointer field so that all of the commands in the 
+     * various queues are valid.
+     */
+
+    if (cmd->use_sg) {
+	cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+	cmd->SCp.buffers_residual = cmd->use_sg - 1;
+	cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+	cmd->SCp.this_residual = cmd->SCp.buffer->length;
+    } else {
+	cmd->SCp.buffer = NULL;
+	cmd->SCp.buffers_residual = 0;
+	cmd->SCp.ptr = (char *) cmd->request_buffer;
+	cmd->SCp.this_residual = cmd->request_bufflen;
+    }
+}
+
+#include <linux/delay.h>
+
+#ifdef NDEBUG
+static struct {
+    unsigned char mask;
+    char * name;} 
+signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" }, 
+    { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD,  "CD" }, { SR_IO, "IO" }, 
+    { SR_SEL, "SEL" }, {0, NULL}}, 
+basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
+icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
+    {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"}, 
+    {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"}, 
+    {0, NULL}},
+mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"}, 
+    {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR, 
+    "MODE PARITY INTR"}, {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+    {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"}, 
+    {0, NULL}};
+
+/*
+ * Function : void NCR5380_print(struct Scsi_Host *instance)
+ *
+ * Purpose : print the SCSI bus signals for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance) {
+    NCR5380_local_declare();
+    unsigned char status, data, basr, mr, icr, i;
+    NCR5380_setup(instance);
+    cli();
+    data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+    status = NCR5380_read(STATUS_REG);
+    mr = NCR5380_read(MODE_REG);
+    icr = NCR5380_read(INITIATOR_COMMAND_REG);
+    basr = NCR5380_read(BUS_AND_STATUS_REG);
+    sti();
+    for (i = 0; signals[i].mask ; ++i) 
+	if (status & signals[i].mask)
+	    printk(" %s", signals[i].name);
+    for (i = 0; basrs[i].mask ; ++i) 
+	if (basr & basrs[i].mask)
+	    printk(" %s", basrs[i].name);
+    for (i = 0; icrs[i].mask; ++i) 
+	if (icr & icrs[i].mask)
+	    printk(" %s", icrs[i].name);
+    for (i = 0; mrs[i].mask; ++i) 
+	if (mr & mrs[i].mask)
+	    printk(" %s", mrs[i].name);
+    printk("\n");
+}
+
+static struct {
+    unsigned char value;
+    char *name;
+} phases[] = {
+{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+{PHASE_UNKNOWN, "UNKNOWN"}};
+
+/* 
+ * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance) {
+    NCR5380_local_declare();
+    unsigned char status;
+    int i;
+    NCR5380_setup(instance);
+
+    status = NCR5380_read(STATUS_REG);
+    if (!(status & SR_REQ)) 
+	printk("scsi%d: REQ not asserted, phase unknown.\n", 
+	    instance->host_no);
+    else {
+	for (i = 0; (phases[i].value != PHASE_UNKNOWN) && 
+	    (phases[i].value != (status & PHASE_MASK)); ++i); 
+	printk("scsi%d: phase %s\n", instance->host_no, phases[i].name);
+    }
+}
+#endif
+
+/*
+ * We need to have our coroutine active given these constraints : 
+ * 1.  The mutex flag, main_running, can only be set when the main 
+ *     routine can actually process data, otherwise SCSI commands
+ *     will never get issued.
+ *
+ * 2.  NCR5380_main() shouldn't be called before it has exited, because
+ *     other drivers have had kernel stack overflows in similar
+ *     situations.
+ *
+ * 3.  We don't want to inline NCR5380_main() because of space concerns,
+ *     even though it is only called in two places.
+ *
+ * So, the solution is to set the mutex in an inline wrapper for the 
+ * main coroutine, and have the main coroutine exit with interrupts 
+ * disabled after the final search through the queues so that no race 
+ * conditions are possible.
+ */
+
+static volatile int main_running = 0;
+
+/* 
+ * Function : run_main(void)
+ * 
+ * Purpose : insure that the coroutine is running and will process our 
+ * 	request.  main_running is checked/set here (in an inline function)
+ *	rather than in NCR5380_main itself to reduce the chances of stack
+ *	overflow.
+ *
+ */
+
+static __inline__ void run_main(void) {
+    cli();
+    if (!main_running) {
+	main_running = 1;
+        NCR5380_main();
+	/* 
+         * main_running is cleared in NCR5380_main once it can't do 
+	 * more work, and NCR5380_main exits with interrupts disabled.
+	 */
+	sti();
+    } else 
+	sti();
+}
+
+#ifdef USLEEP
+#ifndef NCR5380_TIMER
+#error "NCR5380_TIMER must be defined so that this type of NCR5380 driver gets a unique timer."
+#endif
+
+/*
+ * These need tweaking, and would probably work best as per-device 
+ * flags initialized differently for disk, tape, cd, etc devices.
+ * People with broken devices are free to experiment as to what gives
+ * the best results for them.
+ *
+ * USLEEP_SLEEP should be a minimum seek time.
+ *
+ * USLEEP_POLL should be a maximum rotational latency.
+ */
+#ifndef USLEEP_SLEEP
+/* 20 ms (reasonable hard disk speed) */
+#define USLEEP_SLEEP 2
+#endif
+/* 300 RPM (floppy speed) */
+#ifndef USLEEP_POLL
+#define USLEEP_POLL 20
+#endif
+
+static struct Scsi_Host * expires_first = NULL;
+
+/* 
+ * Function : int should_disconnect (unsigned char cmd)
+ *
+ * Purpose : decide weather a command would normally disconnect or 
+ *	not, since if it won't disconnect we should go to sleep.
+ *
+ * Input : cmd - opcode of SCSI command
+ *
+ * Returns : DISCONNECT_LONG if we should disconnect for a really long 
+ * 	time (ie always, sleep, look for REQ active, sleep), 
+ *	DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal
+ * 	time-to-data delay, DISCONNECT_NONE if this command would return
+ * 	immediately.
+ *
+ *      Future sleep algorithms based on time to data can exploit 
+ *      something like this so they can differentiate between "normal" 
+ *	(ie, read, write, seek) and unusual commands (ie, * format).
+ *
+ * Note : We don't deal with commands that handle an immediate disconnect,
+ *        
+ */
+
+static int should_disconnect (unsigned char cmd) {
+    switch (cmd) {
+    case READ_6:
+    case WRITE_6:
+    case SEEK_6:
+    case READ_10:
+    case WRITE_10:
+    case SEEK_10:
+	return DISCONNECT_TIME_TO_DATA;
+    case FORMAT_UNIT:
+    case SEARCH_HIGH:
+    case SEARCH_LOW:
+    case SEARCH_EQUAL:
+	return DISCONNECT_LONG;
+    default:
+	return DISCONNECT_NONE;
+    }
+}
+
+/*
+ * Assumes instance->time_expires has been set in higher level code.
+ */
+
+static int NCR5380_set_timer (struct Scsi_Host *instance) {
+    struct Scsi_Host *tmp, **prev;
+    
+    cli();
+    if (((struct NCR5380_hostdata *) (instance->host_data))->next_timer) {
+	sti();
+	return -1;
+    }
+
+    for (prev = &expires_first, tmp = expires_first; tmp; 
+	prev = &(((struct NCR5380_hostdata *) tmp->host_data)->next_timer), 
+	tmp = ((struct NCR5380_hostdata *) tmp->host_data)->next_timer)
+	if (instance->time_expires < tmp->time_expires) 
+	    break;
+	   
+    instance->next_timer = tmp;
+    *prev = instance;
+    timer_table[NCR5380_TIMER].expires = expires_first->time_expires;
+    timer_active |= 1 << NCR5380_TIMER;
+    sti();
+    return 0;
+}    
+
+/* Doing something about unwanted reentrancy here might be useful */
+void NCR5380_timer_fn(void) {
+    struct Scsi_Host *instance;
+    cli();
+    for (; expires_first && expires_first->time_expires >= jiffies; ) {
+	instance = ((NCR5380_hostdata *) expires_first->host_data)->
+	    expires_next;
+	((NCR5380_hostdata *) expires_first->host_data)->expires_next = 
+	    NULL;
+	((NCR5380_hostdata *) expires_first->host_data)->time_expires = 
+	    0;
+	expires_first = instance;
+    }
+
+    if (expires_first) {
+	timer_table[NCR5380_TIMER].expires = ((NCR5380_hostdata *) 
+	    expires_first->host_data)->time_expires;
+	timer_active |= (1 << NCR5380_TIMER);
+    } else {
+	timer_table[NCR5380_TIMER].expires = 0;
+	timer_active &= ~(1 << MCR5380_TIMER);
+    }
+    sti();
+
+    run_main();
+}
+#endif /* def USLEEP */
+
+static void NCR5380_all_init (void) {
+    static int done = 0;
+    if (!done) {
+#if (NDEBUG & NDEBUG_INIT)
+	printk("scsi : NCR5380_all_init()\n");
+#endif
+	done = 1;
+#ifdef USLEEP
+	timer_table[NCR5380_TIMER].expires = 0;
+	timer_table[NCR5380_TIMER].fn = NCR5380_timer_fn;
+#endif
+    }
+}
+
+#ifdef AUTOPROBE_IRQ
+/*
+ * Function : int NCR5380_probe_irq (struct Scsi_Host *instance, int possible)
+ * 
+ * Purpose : autoprobe for the IRQ line used by the NCR5380.  
+ *
+ * Inputs : instance - pointer to this instance of the NCR5380 driver,
+ *          possible - bitmask of permissible interrupts.
+ *
+ * Returns : number of the IRQ selected, IRQ_NONE if no interrupt fired.
+ * 
+ * XXX no effort is made to deal with spurious interrupts. 
+ */
+
+
+static int probe_irq;
+static void probe_intr (int irq, struct pt_regs * regs) {
+    probe_irq = irq;
+};
+
+static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) {
+    NCR5380_local_declare();
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+	 instance->hostdata;
+    unsigned long timeout;
+    int trying_irqs, i, mask;
+    NCR5380_setup(instance);
+
+    for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1) 
+	if ((mask & possible) &&  (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe") 
+	    == 0)) 
+	    trying_irqs |= mask;
+
+    timeout = jiffies + 25;
+    probe_irq = IRQ_NONE;
+
+/*
+ * A interrupt is triggered whenever BSY = false, SEL = true
+ * and a bit set in the SELECT_ENABLE_REG is asserted on the 
+ * SCSI bus.
+ *
+ * Note that the bus is only driven when the phase control signals
+ * (I/O, C/D, and MSG) match those in the TCR, so we must reset that
+ * to zero.
+ */
+
+    NCR5380_write(TARGET_COMMAND_REG, 0);
+    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+    NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | 
+	ICR_ASSERT_SEL);
+
+    while (probe_irq == IRQ_NONE && jiffies < timeout);
+
+    NCR5380_write(SELECT_ENABLE_REG, 0);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+	if (trying_irqs & mask) 
+	    free_irq(i);
+
+    return probe_irq;
+}
+#endif /* AUTOPROBE_IRQ */
+ 
+/*
+ * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+ *
+ * Purpose : called by probe code indicating the NCR5380 driver
+ *	     options that were selected.
+ *
+ * Inputs : instance, pointer to this instance.  Unused.
+ */
+
+static void NCR5380_print_options (struct Scsi_Host *instance) {
+    printk(" generic options"
+#ifdef AUTOPROBE_IRQ
+    " AUTOPROBE_IRQ"
+#endif
+#ifdef AUTOSENSE 
+    " AUTOSENSE"
+#endif
+#ifdef DIFFERENTIAL
+    " DIFFERENTIAL"
+#endif
+#ifdef REAL_DMA
+    " REAL DMA"
+#endif
+#ifdef REAL_DMA_POLL
+    " REAL DMA POLL"
+#endif
+#ifdef PARITY
+    " PARITY"
+#endif
+#ifdef PSEUDO_DMA
+    " PSEUDO DMA"
+#endif
+#ifdef SCSI2
+    " SCSI-2"
+#endif
+#ifdef UNSAFE
+    " UNSAFE "
+#endif
+    );
+#ifdef USLEEP
+    printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
+#endif
+    printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+#ifdef NCR53C400
+    if (hostdata->flags & FLAG_NCR53C400) {
+	printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
+    }
+#endif
+}
+
+/*
+ * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues, called from
+ *	NCR5380_abort and NCR5380_debug to aid debugging.
+ *
+ * Inputs : instance, pointer to this instance.  
+ */
+
+static void NCR5380_print_status (struct Scsi_Host *instance) {
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
+	instance->hostdata;
+    Scsi_Cmnd *ptr;
+
+
+    printk("NCR5380 : coroutine is%s running.\n",
+	main_running ? "" : "n't");
+    
+#ifdef NDEBUG
+    NCR5380_print (instance);
+    NCR5380_print_phase (instance);
+#endif
+
+    cli();
+    if (!hostdata->connected) {
+	printk ("scsi%d: no currently connected command\n",
+	    instance->host_no);
+    } else {
+	print_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected);
+    }
+
+    printk ("scsi%d: issue_queue\n", instance->host_no);
+
+    for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; 
+	ptr = (Scsi_Cmnd *) ptr->host_scribble) 
+	print_Scsi_Cmnd (ptr);
+
+    printk ("scsi%d: disconnected_queue\n", instance->host_no);
+
+    for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; 
+	ptr = (Scsi_Cmnd *) ptr->host_scribble) 
+	print_Scsi_Cmnd (ptr);
+    
+    sti();
+}
+
+
+/* 
+ * Function : void NCR5380_init (struct Scsi_Host *instance, flags)
+ *
+ * Purpose : initializes *instance and corresponding 5380 chip,
+ *	with flags OR'd into the initial flags value.
+ *
+ * Inputs : instance - instantiation of the 5380 driver.  
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ * 	set correctly.  I don't care about the irq and other fields. 
+ * 
+ */
+
+static void NCR5380_init (struct Scsi_Host *instance, int flags) {
+    NCR5380_local_declare();
+    int i;
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
+	instance->hostdata;
+
+    /* 
+     * On NCR53C400 boards, NCR5380 registers are mapped 8 past 
+     * the base address.
+     */
+
+    if (flags & FLAG_NCR53C400)
+	instance->io_port += 8;
+
+    NCR5380_setup(instance);
+
+    NCR5380_all_init();
+
+    hostdata->aborted = 0;
+    hostdata->id_mask = 1 << instance->this_id;
+    for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+	if (i > hostdata->id_mask)
+	    hostdata->id_higher_mask |= i;
+    for (i = 0; i < 8; ++i)
+	hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+    hostdata->dmalen = 0;
+#endif
+    hostdata->targets_present = 0;
+    hostdata->connected = NULL;
+    hostdata->issue_queue = NULL;
+    hostdata->disconnected_queue = NULL;
+    hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
+
+    if (!the_template) {
+	the_template = instance->hostt;
+	first_instance = instance;
+    }
+	
+
+#ifdef USLEEP
+    hostdata->time_expires = 0;
+    hostdata->next_timer = NULL;
+#endif
+
+#ifndef AUTOSENSE
+    if ((instance->cmd_per_lun > 1) || instance->can_queue > 1)) 
+	 printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
+	        "        without AUTOSENSE option, contingent allegiance conditions may\n"
+	        "        be incorrectly cleared.\n", instance->host_no);
+#endif /* def AUTOSENSE */
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+    NCR5380_write(MODE_REG, MR_BASE);
+    NCR5380_write(TARGET_COMMAND_REG, 0);
+    NCR5380_write(SELECT_ENABLE_REG, 0);
+#ifdef NCR53C400
+    if (hostdata->flags & FLAG_NCR53C400) {
+	NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+    }
+#endif
+}
+
+/* 
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, 
+ *	void (*done)(Scsi_Cmnd *)) 
+ *
+ * Purpose :  enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ *	a pointer to the command descriptor.
+ * 
+ * Returns : 0
+ *
+ * Side effects : 
+ *      cmd is added to the per instance issue_queue, with minor 
+ *	twiddling done to the host specific fields of cmd.  If the 
+ *	main coroutine is not running, it is restarted.
+ *
+ */
+
+/* Only make static if a wrapper function is used */
+#ifndef NCR5380_queue_command
+static
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) {
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+	cmd->host->hostdata;
+    Scsi_Cmnd *tmp;
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+    switch (cmd->cmnd[0]) {
+    case WRITE:
+    case WRITE_10:
+	printk("scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
+	    instance->host_no);
+	cmd->result = (DID_ERROR << 16);
+	done(cmd);
+	return 0;
+    }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+
+    /* 
+     * We use the host_scribble field as a pointer to the next command  
+     * in a queue 
+     */
+
+    cmd->host_scribble = NULL;
+    cmd->scsi_done = done;
+
+    cmd->result = 0;
+
+
+    /* 
+     * Insert the cmd into the issue queue. Note that REQUEST SENSE 
+     * commands are added to the head of the queue since any command will
+     * clear the contingent allegiance condition that exists and the 
+     * sense data is only guaranteed to be valid while the condition exists.
+     */
+
+    cli();
+    if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+	cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
+	hostdata->issue_queue = cmd;
+    } else {
+	for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; 
+		tmp = (Scsi_Cmnd *) tmp->host_scribble);
+	tmp->host_scribble = (unsigned char *) cmd;
+    }
+#if (NDEBUG & NDEBUG_QUEUES)
+    printk("scsi%d: command added to %s of queue\n", instance->host_no,
+	(cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+#endif
+
+/* Run the coroutine if it isn't already running. */
+    run_main();
+    return 0;
+}
+
+/*
+ * Function : NCR5380_main (void) 
+ *
+ * Purpose : NCR5380_main is a coroutine that runs as long as more work can 
+ *	be done on the NCR5380 host adapters in a system.  Both 
+ *	NCR5380_queue_command() and NCR5380_intr() will try to start it 
+ *	in case it is not running.
+ * 
+ * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should 
+ *  reenable them.  This prevents reentrancy and kernel stack overflow.
+ */ 	
+    
+static void NCR5380_main (void) {
+    Scsi_Cmnd *tmp, *prev;
+    struct Scsi_Host *instance;
+    struct NCR5380_hostdata *hostdata;
+    int done;
+
+    /*
+     * We run (with interrupts disabled) until we're sure that none of 
+     * the host adapters have anything that can be done, at which point 
+     * we set main_running to 0 and exit.
+     *
+     * Interrupts are enabled before doing various other internal 
+     * instructions, after we've decided that we need to run through
+     * the loop again.
+     *
+     * this should prevent any race conditions.
+     */
+
+    do {
+	cli(); /* Freeze request queues */
+	done = 1;
+	for (instance = first_instance; instance && 
+	    instance->hostt == the_template; instance=instance->next) {
+	    hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+	    cli();
+	    if (!hostdata->connected) {
+#if (NDEBUG & NDEBUG_MAIN)
+		printk("scsi%d: not connected\n", instance->host_no);
+#endif
+		/*
+		 * Search through the issue_queue for a command destined
+		 * for a target that's not busy.
+		 */
+		for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, 
+		    prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) 
+		    tmp->host_scribble) 
+
+		    /*  When we find one, remove it from the issue queue. */
+		    if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
+			    if (prev)
+				prev->host_scribble = tmp->host_scribble;
+			    else
+				hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
+			tmp->host_scribble = NULL;
+
+			/* reenable interrupts after finding one */
+			sti();
+
+			/* 
+			 * Attempt to establish an I_T_L nexus here. 
+			 * On success, instance->hostdata->connected is set.
+			 * On failure, we must add the command back to the
+			 *   issue queue so we can keep trying.	
+			 */
+#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
+			printk("scsi%d: main() : command for target %d lun %d removed from issue_queue\n",
+			    instance->host_no, tmp->target, tmp->lun);
+#endif
+    			/* 
+    			 * REQUEST SENSE commands are issued without tagged
+    			 * queueing, even on SCSI-II devices because the 
+    			 * contingent allegiance condition exists for the 
+    			 * entire unit.
+    			 */
+    			
+			if (!NCR5380_select(instance, tmp, 
+			    (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : 
+			    TAG_NEXT)) {
+			    break;
+			} else {
+			    cli();
+			    tmp->host_scribble = (unsigned char *) 
+				hostdata->issue_queue;
+			    hostdata->issue_queue = tmp;
+			    sti();
+#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
+			printk("scsi%d: main(): select() failed, returned to issue_queue\n",
+			    instance->host_no);
+#endif
+			}
+		    } /* if target/lun is not busy */
+	    } /* if (!hostdata->connected) */
+		
+	    if (hostdata->connected 
+#ifdef REAL_DMA
+		&& !hostdata->dmalen
+#endif
+#ifdef USLEEP
+		&& (!hostdata->time_expires || hostdata->time_expires >= jiffies)
+#endif
+		) {
+		sti();
+#if (NDEBUG & NDEBUG_MAIN)
+		printk("scsi%d: main() : performing information transfer\n",
+			instance->host_no);
+#endif
+		NCR5380_information_transfer(instance);
+#if (NDEBUG & NDEBUG_MAIN)
+		printk("scsi%d: main() : done set false\n", instance->host_no);
+#endif
+		done = 0;
+	    } else 
+		break;
+	} /* for instance */
+    } while (!done);
+    main_running = 0;
+}
+
+/*
+ * Function : void NCR5380_intr (int irq)
+ * 
+ * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ *	from the disconnected queue, and restarting NCR5380_main() 
+ *	as required.
+ *
+ * Inputs : int irq, irq that caused this interrupt.
+ *
+ */
+
+static void NCR5380_intr (int irq, struct pt_regs * regs) {
+    NCR5380_local_declare(); 
+    struct Scsi_Host *instance;
+    int done;
+    unsigned char basr;
+#if (NDEBUG & NDEBUG_INTR)
+    printk("scsi : NCR5380 irq %d triggered\n", irq);
+#endif
+    do {
+	done = 1;
+	for (instance = first_instance; instance && (instance->hostt == 
+	    the_template); instance = instance->next)
+	    if (instance->irq == irq) {
+		
+		/* Look for pending interrupts */
+		NCR5380_setup(instance);
+		basr = NCR5380_read(BUS_AND_STATUS_REG);
+		/* XXX dispatch to appropriate routine if found and done=0 */
+		if (basr & BASR_IRQ) {
+#if (NDEBUG & NDEBUG_INTR)
+		    NCR5380_print(instance);
+#endif
+		    if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == 
+			(SR_SEL | SR_IO)) {
+			done = 0;
+			sti();
+#if (NDEBUG & NDEBUG_INTR)
+			printk("scsi%d: SEL interrupt\n", instance->host_no);
+#endif
+			NCR5380_reselect(instance);
+			(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+		    } else if (basr & BASR_PARITY_ERROR) {
+#if (NDEBUG & NDEBUG_INTR)
+			printk("scsi%d: PARITY interrupt\n", instance->host_no);
+#endif
+			(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ 		    } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+#if (NDEBUG & NDEBUG_INTR)
+ 			printk("scsi%d: RESET interrupt\n", instance->host_no);
+#endif
+			(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+		    } else {
+/*  
+ * XXX the rest of the interrupt conditions should *only* occur during a 
+ * DMA transfer, which I haven't gotten around to fixing yet.
+ */
+
+#if defined(REAL_DMA)
+		    /*
+		     * We should only get PHASE MISMATCH and EOP interrupts
+		     * if we have DMA enabled, so do a sanity check based on
+		     * the current setting of the MODE register.
+		     */
+
+			if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr & 
+			    BASR_END_DMA_TRANSFER) || 
+			    !(basr & BASR_PHASE_MATCH))) {
+			    int transfered;
+
+			    if (!hostdata->connected) 
+				panic("scsi%d: received end of DMA interrupt with no connected cmd\n",
+				    instance->hostno);
+
+			    transfered = (hostdata->dmalen - NCR5380_dma_residual(instance));
+			    hostdata->connected->SCp.this_residual -= transferred;
+			    hostdata->connected->SCp.ptr += transferred;
+			    hostdata->dmalen = 0;
+
+			    (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#if NCR_TIMEOUT
+			    {
+			      unsigned long timeout = jiffies + NCR_TIMEOUT;
+
+			      while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK
+				     && jiffies < timeout)
+				;
+			      if (jiffies >= timeout)
+			        printk("scsi: timeout at %d\n", __LINE__);
+			    }
+#else /* NCR_TIMEOUT */
+			    while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+#endif
+
+			    NCR5380_write(MODE_REG, MR_BASE);
+			    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+			}
+#else
+#if (NDEBUG & NDEBUG_INTR)
+		    printk("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
+#endif
+		    (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#endif
+		    } 
+		} /* if BASR_IRQ */
+		if (!done) 
+		    run_main();
+	    } /* if (instance->irq == irq) */
+    } while (!done);
+}
+
+/* 
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, 
+ *	int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ *	including ARBITRATION, SELECTION, and initial message out for 
+ *	IDENTIFY and queue messages. 
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this 
+ * 	target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for 
+ *	new tag, TAG_NONE for untagged queueing, otherwise set to the tag for 
+ *	the command that is presently connected.
+ * 
+ * Returns : -1 if selection could not execute for some reason,
+ *	0 if selection succeeded or failed because the target 
+ * 	did not respond.
+ *
+ * Side effects : 
+ * 	If bus busy, arbitration failed, etc, NCR5380_select() will exit 
+ *		with registers as they should have been on entry - ie
+ *		SELECT_ENABLE will be set appropriately, the NCR5380
+ *		will cease to drive any SCSI bus signals.
+ *
+ *	If successful : I_T_L or I_T_L_Q nexus will be established, 
+ *		instance->connected will be set to cmd.  
+ * 		SELECT interrupt will be disabled.
+ *
+ *	If failed (no target) : cmd->scsi_done() will be called, and the 
+ *		cmd->result host byte set to DID_BAD_TARGET.
+ */
+
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+    int tag) {
+    NCR5380_local_declare();
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata*) 
+	instance->hostdata;
+    unsigned char tmp[3], phase;
+    unsigned char *data;
+    int len;
+    unsigned long timeout;
+    NCR5380_setup(instance);
+
+    hostdata->restart_select = 0;
+#if defined (NDEBUG) && (NDEBUG & NDEBUG_ARBITRATION) 
+    NCR5380_print(instance);
+    printk("scsi%d: starting arbitration, id = %d\n", instance->host_no,
+	instance->this_id);
+#endif
+    cli(); 
+
+    /* 
+     * Set the phase bits to 0, otherwise the NCR5380 won't drive the 
+     * data bus during SELECTION.
+     */
+
+    NCR5380_write(TARGET_COMMAND_REG, 0);
+
+
+    /* 
+     * Start arbitration.
+     */
+    
+    NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+    NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+    sti();
+
+    /* Wait for arbitration logic to complete */
+#if NCR_TIMEOUT
+    {
+      unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+      while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+	   && jiffies < timeout)
+	;
+      if (jiffies >= timeout)
+      {
+	printk("scsi: arbitration timeout at %d\n", __LINE__);
+	NCR5380_write(MODE_REG, MR_BASE);
+	NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+	return -1;
+      }
+    }
+#else /* NCR_TIMEOUT */
+    while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS));
+#endif
+
+#if (NDEBUG & NDEBUG_ARBITRATION) 
+    printk("scsi%d: arbitration complete\n", instance->host_no);
+/* Avoid GCC 2.4.5 asm needs to many reloads error */
+    __asm__("nop");
+#endif
+
+    /* 
+     * The arbitration delay is 2.2us, but this is a minimum and there is 
+     * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+     * the integral nature of udelay().
+     *
+     */
+
+    udelay(3);
+
+    /* Check for lost arbitration */
+    if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+	(NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
+	(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
+	NCR5380_write(MODE_REG, MR_BASE); 
+#if (NDEBUG & NDEBUG_ARBITRATION)
+    printk("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n", 
+	instance->host_no);
+#endif
+	return -1;
+    }
+
+
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
+    
+    if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) {
+	NCR5380_write(MODE_REG, MR_BASE);
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+#if (NDEBUG & NDEBUG_ARBITRATION)
+    printk("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n", 
+	instance->host_no);
+#endif
+	return -1;
+    }
+
+    /* 
+     * Again, bus clear + bus settle time is 1.2us, however, this is 
+     * a minimum so we'll udelay ceil(1.2)
+     */
+
+    udelay(2);	
+
+#if (NDEBUG & NDEBUG_ARBITRATION)
+    printk("scsi%d: won arbitration\n", instance->host_no);
+#endif
+
+
+    /* 
+     * Now that we have won arbitration, start Selection process, asserting 
+     * the host and target ID's on the SCSI bus.
+     */
+
+    NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
+
+    /* 
+     * Raise ATN while SEL is true before BSY goes false from arbitration,
+     * since this is the only way to guarantee that we'll get a MESSAGE OUT
+     * phase immediately after selection.
+     */
+
+    NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | 
+	ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
+    NCR5380_write(MODE_REG, MR_BASE);
+
+    /* 
+     * Reselect interrupts must be turned off prior to the dropping of BSY,
+     * otherwise we will trigger an interrupt.
+     */
+    NCR5380_write(SELECT_ENABLE_REG, 0);
+
+    /* Reset BSY */
+    NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | 
+	ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+    /* 
+     * Something weird happens when we cease to drive BSY - looks
+     * like the board/chip is letting us do another read before the 
+     * appropriate propagation delay has expired, and we're confusing
+     * a BSY signal from ourselves as the target's response to SELECTION.
+     *
+     * A small delay (the 'C++' frontend breaks the pipeline with an
+     * unnecessary jump, making it work on my 386-33/Trantor T128, the
+     * tighter 'C' code breaks and requires this) solves the problem - 
+     * the 1 us delay is arbitrary, and only used because this delay will 
+     * be the same on other platforms and since it works here, it should 
+     * work there.
+     */
+
+    udelay(1);
+
+#if (NDEBUG & NDEBUG_SELECTION)
+    printk("scsi%d: selecting target %d\n", instance->host_no, cmd->target);
+#endif
+
+    /* 
+     * The SCSI specification calls for a 250 ms timeout for the actual 
+     * selection.
+     */
+
+    timeout = jiffies + 25; 
+
+    /* 
+     * XXX very interesting - we're seeing a bounce where the BSY we 
+     * asserted is being reflected / still asserted (propagation delay?)
+     * and it's detecting as true.  Sigh.
+     */
+
+    while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & 
+	(SR_BSY | SR_IO)));
+
+    if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == 
+	    (SR_SEL | SR_IO)) {
+	    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+	    NCR5380_reselect(instance);
+	    printk ("scsi%d: reselection after won arbitration?\n",
+		instance->host_no);
+	    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+	    return -1;
+    }
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+    if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+	if (hostdata->targets_present & (1 << cmd->target)) {
+	    printk("scsi%d: weirdness\n", instance->host_no);
+	    if (hostdata->restart_select)
+		printk("\trestart select\n");
+#ifdef NDEBUG
+	    NCR5380_print (instance);
+#endif
+	    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+	    return -1;
+	}
+	cmd->result = DID_BAD_TARGET << 16;
+	cmd->scsi_done(cmd);
+	NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+#if (NDEBUG & NDEBUG_SELECTION)
+	printk("scsi%d: target did not respond within 250ms\n", 
+	    instance->host_no);
+#endif
+	NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+	return 0;
+    } 
+
+    hostdata->targets_present |= (1 << cmd->target);
+
+    /*
+     * Since we followed the SCSI spec, and raised ATN while SEL 
+     * was true but before BSY was false during selection, the information
+     * transfer phase should be a MESSAGE OUT phase so that we can send the
+     * IDENTIFY message.
+     * 
+     * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+     * message (2 bytes) with a tag ID that we increment with every command
+     * until it wraps back to 0.
+     *
+     * XXX - it turns out that there are some broken SCSI-II devices,
+     *	     which claim to support tagged queuing but fail when more than
+     *	     some number of commands are issued at once.
+     */
+
+    /* Wait for start of REQ/ACK handshake */
+    while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+#if (NDEBUG & NDEBUG_SELECTION)
+    printk("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
+	instance->host_no, cmd->target);
+#endif
+    tmp[0] = IDENTIFY(((instance->irq == IRQ_NONE) ? 0 : 1), cmd->lun);
+#ifdef SCSI2
+    if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
+	tmp[1] = SIMPLE_QUEUE_TAG;
+	if (tag == TAG_NEXT) {
+	    /* 0 is TAG_NONE, used to imply no tag for this command */
+	    if (cmd->device->current_tag == 0)
+		cmd->device->current_tag = 1;
+
+	    cmd->tag = cmd->device->current_tag;
+	    cmd->device->current_tag++;
+	} else  
+	    cmd->tag = (unsigned char) tag;
+
+	tmp[2] = cmd->tag;
+	hostdata->last_message = SIMPLE_QUEUE_TAG;
+	len = 3;
+    } else 
+#endif /* def SCSI2 */
+    {
+	len = 1;
+	cmd->tag=0;
+    }
+
+    /* Send message(s) */
+    data = tmp;
+    phase = PHASE_MSGOUT;
+    NCR5380_transfer_pio(instance, &phase, &len, &data);
+#if (NDEBUG & NDEBUG_SELECTION)
+    printk("scsi%d: nexus established.\n", instance->host_no);
+#endif
+    /* XXX need to handle errors here */
+    hostdata->connected = cmd;
+#ifdef SCSI2
+    if (!cmd->device->tagged_queue)
+#endif    
+	hostdata->busy[cmd->target] |= (1 << cmd->lun);
+
+    initialize_SCp(cmd);
+
+
+    return 0;
+}
+
+/* 
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance, 
+ *      unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to 
+ *	what phase is expected, *count - pointer to number of 
+ *	bytes to transfer, **data - pointer to data pointer.
+ * 
+ * Returns : -1 when different phase is entered without transferring
+ *	maximum number of bytes, 0 if all bytes or transfered or exit
+ *	is in same phase.
+ *
+ * 	Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it 
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio (struct Scsi_Host *instance, 
+	unsigned char *phase, int *count, unsigned char **data) {
+    NCR5380_local_declare();
+    register unsigned char p = *phase, tmp;
+    register int c = *count;
+    register unsigned char *d = *data;
+    NCR5380_setup(instance);
+
+    /* 
+     * The NCR5380 chip will only drive the SCSI bus when the 
+     * phase specified in the appropriate bits of the TARGET COMMAND
+     * REGISTER match the STATUS REGISTER
+     */
+
+    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+    do {
+	/* 
+	 * Wait for assertion of REQ, after which the phase bits will be 
+	 * valid 
+	 */
+	while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+#if (NDEBUG & NDEBUG_HANDSHAKE)
+	printk("scsi%d: REQ detected\n", instance->host_no);
+#endif
+
+	/* Check for phase mismatch */	
+	if ((tmp & PHASE_MASK) != p) {
+#if (NDEBUG & NDEBUG_PIO)
+	    printk("scsi%d: phase mismatch\n", instance->host_no);
+	    NCR5380_print_phase(instance);
+#endif
+	    break;
+	}
+
+	/* Do actual transfer from SCSI bus to / from memory */
+	if (!(p & SR_IO)) 
+	    NCR5380_write(OUTPUT_DATA_REG, *d);
+	else 
+	    *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+	++d;
+
+	/* 
+	 * The SCSI standard suggests that in MSGOUT phase, the initiator
+	 * should drop ATN on the last byte of the message phase
+	 * after REQ has been asserted for the handshake but before
+	 * the initiator raises ACK.
+	 */
+
+	if (!(p & SR_IO)) {
+	    if (!((p & SR_MSG) && c > 1)) {
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+		    ICR_ASSERT_DATA);
+#if (NDEBUG & NDEBUG_PIO)
+	NCR5380_print(instance);
+#endif
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+			ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+	    } else {
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+		    ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+#if (NDEBUG & NDEBUG_PIO)
+	NCR5380_print(instance);
+#endif
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+		    ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+	    }
+	} else {
+#if (NDEBUG & NDEBUG_PIO)
+	NCR5380_print(instance);
+#endif
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+	}
+
+	while (NCR5380_read(STATUS_REG) & SR_REQ);
+
+#if (NDEBUG & NDEBUG_HANDSHAKE)
+	    printk("scsi%d: req false, handshake complete\n", instance->host_no);
+#endif
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking : 
+ * 1.  We were in MSGOUT phase, and we are on the last byte of the 
+ *	message.  ATN must be dropped as ACK is dropped.
+ *
+ * 2.  We are in a MSGIN phase, and we are on the last byte of the  
+ *	message.  We must exit with ACK asserted, so that the calling
+ *	code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3.  ACK and ATN are clear and the target may proceed as normal.
+ */
+	if (!(p == PHASE_MSGIN && c == 1)) {  
+	    if (p == PHASE_MSGOUT && c > 1)
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+	    else
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+	} 
+    } while (--c);
+
+#if (NDEBUG & NDEBUG_PIO) 
+    printk("scsi%d: residual %d\n", instance->host_no, c);
+#endif
+
+    *count = c;
+    *data = d;
+    tmp = NCR5380_read(STATUS_REG);
+    if (tmp & SR_REQ)
+	*phase = tmp & PHASE_MASK;
+    else 
+	*phase = PHASE_UNKNOWN;
+
+    if (!c || (*phase == p))
+	return 0;
+    else 
+	return -1;
+}
+
+#if defined(REAL_DMA) || defined(PSEUDO_DMA) || defined (REAL_DMA_POLL)
+/* 
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance, 
+ *      unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ *	or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to 
+ *	what phase is expected, *count - pointer to number of 
+ *	bytes to transfer, **data - pointer to data pointer.
+ * 
+ * Returns : -1 when different phase is entered without transferring
+ *	maximum number of bytes, 0 if all bytes or transfered or exit
+ *	is in same phase.
+ *
+ * 	Also, *phase, *count, *data are modified in place.
+ *
+ */
+
+
+static int NCR5380_transfer_dma (struct Scsi_Host *instance, 
+    unsigned char *phase, int *count, unsigned char **data) {
+    NCR5380_local_declare();
+    register int c = *count;
+    register unsigned char p = *phase;
+    register unsigned char *d = *data;
+    unsigned char tmp;
+    int foo;
+#if defined(REAL_DMA_POLL)
+    int cnt, toPIO;
+    unsigned char saved_data = 0, overrun = 0, residue;
+#endif
+
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
+	instance->hostdata;
+
+    NCR5380_setup(instance);
+
+    if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
+        *phase = tmp;
+        return -1;
+    }
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL) 
+#ifdef READ_OVERRUNS
+     if (p & SR_IO) {
+       c -= 2;
+     }
+#endif
+#if (NDEBUG & NDEBUG_DMA)
+    printk("scsi%d: initializing DMA channel %d for %s, %d bytes %s %0x\n",
+	instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" :
+	"writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d);
+#endif
+    hostdata->dma_len = (p & SR_IO) ?
+	NCR5380_dma_read_setup(instance, d, c) : 
+	NCR5380_dma_write_setup(instance, d, c);
+#endif
+
+    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+#ifdef REAL_DMA
+    NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
+#elif defined(REAL_DMA_POLL)
+    NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#else
+    /*
+     * Note : on my sample board, watch-dog timeouts occurred when interrupts
+     * were not disabled for the duration of a single DMA transfer, from 
+     * before the setting of DMA mode to after transfer of the last byte.
+     */
+
+#if defined(PSEUDO_DMA) && !defined(UNSAFE)
+    cli();
+#endif
+    NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#endif /* def REAL_DMA */
+
+#if (NDEBUG & NDEBUG_DMA) & 0
+    printk("scsi%d: mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG));
+#endif
+
+/* 
+ * FOO stuff. For some UNAPPARENT reason, I'm getting 
+ * watchdog timers fired on bootup for NO APPARENT REASON, meaning it's
+ * probably a timing problem.
+ *
+ * Since this is the only place I have back-to-back writes, perhaps this 
+ * is the problem?
+ */
+
+    if (p & SR_IO) {
+#ifndef FOO
+	udelay(1);
+#endif
+	NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+    } else {
+#ifndef FOO
+	udelay(1);
+#endif
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
+#ifndef FOO
+	udelay(1);
+#endif
+	NCR5380_write(START_DMA_SEND_REG, 0);
+#ifndef FOO
+	udelay(1);
+#endif
+    }
+
+#if defined(REAL_DMA_POLL)
+    do {
+	tmp = NCR5380_read(BUS_AND_STATUS_REG);
+    } while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR | 
+	BASR_END_DMA_TRANSFER)));
+
+/*
+  At this point, either we've completed DMA, or we have a phase mismatch,
+  or we've unexpectedly lost BUSY (which is a real error).
+
+  For write DMAs, we want to wait until the last byte has been
+  transferred out over the bus before we turn off DMA mode.  Alas, there
+  seems to be no terribly good way of doing this on a 5380 under all
+  conditions.  For non-scatter-gather operations, we can wait until REQ
+  and ACK both go false, or until a phase mismatch occurs.  Gather-writes
+  are nastier, since the device will be expecting more data than we
+  are prepared to send it, and REQ will remain asserted.  On a 53C8[01] we
+  could test LAST BIT SENT to assure transfer (I imagine this is precisely
+  why this signal was added to the newer chips) but on the older 538[01]
+  this signal does not exist.  The workaround for this lack is a watchdog;
+  we bail out of the wait-loop after a modest amount of wait-time if
+  the usual exit conditions are not met.  Not a terribly clean or
+  correct solution :-%
+
+  Reads are equally tricky due to a nasty characteristic of the NCR5380.
+  If the chip is in DMA mode for an READ, it will respond to a target's
+  REQ by latching the SCSI data into the INPUT DATA register and asserting
+  ACK, even if it has _already_ been notified by the DMA controller that
+  the current DMA transfer has completed!  If the NCR5380 is then taken
+  out of DMA mode, this already-acknowledged byte is lost.
+
+  This is not a problem for "one DMA transfer per command" reads, because
+  the situation will never arise... either all of the data is DMA'ed
+  properly, or the target switches to MESSAGE IN phase to signal a
+  disconnection (either operation bringing the DMA to a clean halt).
+  However, in order to handle scatter-reads, we must work around the
+  problem.  The chosen fix is to DMA N-2 bytes, then check for the
+  condition before taking the NCR5380 out of DMA mode.  One or two extra
+  bytes are transferred via PIO as necessary to fill out the original
+  request.
+*/
+
+    if (p & SR_IO) {
+#ifdef READ_OVERRUNS
+      udelay(10);
+      if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH|BASR_ACK)) ==
+           (BASR_PHASE_MATCH | BASR_ACK))) {
+        saved_data = NCR5380_read(INPUT_DATA_REGISTER);
+        overrun = 1;
+      }
+#endif
+    } else {
+      int limit = 100;
+      while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) ||
+            (NCR5380_read(STATUS_REG) & SR_REQ)) {
+        if (!(tmp & BASR_PHASE_MATCH)) break;
+        if (--limit < 0) break;
+      }
+    }
+
+
+#if (NDEBUG & NDEBUG_DMA)
+    printk("scsi%d: polled DMA transfer complete, basr 0x%X, sr 0x%X\n",
+	   instance->host_no, tmp, NCR5380_read(STATUS_REG));
+#endif
+
+    NCR5380_write(MODE_REG, MR_BASE);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    residue = NCR5380_dma_residual(instance);
+    c -= residue;
+    *count -= c;
+    *data += c;
+    *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
+
+#ifdef READ_OVERRUNS
+    if (*phase == p && (p & SR_IO) && residue == 0) {
+      if (overrun) {
+#if (NDEBUG & NDEBUG_DMA)
+        printk("Got an input overrun, using saved byte\n");
+#endif
+        **data = saved_data;
+        *data += 1;
+        *count -= 1;
+        cnt = toPIO = 1;
+      } else {
+        printk("No overrun??\n");
+        cnt = toPIO = 2;
+      }
+#if (NDEBUG & NDEBUG_DMA)
+      printk("Doing %d-byte PIO to 0x%X\n", cnt, *data);
+#endif
+      NCR5380_transfer_pio(instance, phase, &cnt, data);
+      *count -= toPIO - cnt;
+    }
+#endif        
+
+#if (NDEBUG & NDEBUG_DMA)
+     printk("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n",
+	    *data, *count, *(*data+*count-1), *(*data+*count));
+#endif
+     return 0;
+     
+#elif defined(REAL_DMA)
+    return 0;
+#else /* defined(REAL_DMA_POLL) */
+    if (p & SR_IO) {
+#if 0
+	if (!(foo = NCR5380_pread(instance, d, c - 1))) {
+	    /*
+	     * We can't disable DMA mode after successfully transferring 
+	     * what we plan to be the last byte, since that would open up
+	     * a race condition where if the target asserted REQ before 
+	     * we got the DMA mode reset, the NCR5380 would have latched
+	     * an additional byte into the INPUT DATA register and we'd
+	     * have dropped it.
+	     * 
+	     * The workaround was to transfer one fewer bytes than we 
+	     * intended to with the pseudo-DMA read function, wait for 
+	     * the chip to latch the last byte, read it, and then disable
+	     * pseudo-DMA mode.
+	     * 
+	     * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
+	     * REQ is deasserted when ACK is asserted, and not reasserted
+	     * until ACK goes false.  Since the NCR5380 won't lower ACK
+	     * until DACK is asserted, which won't happen unless we twiddle
+	     * the DMA port or we take the NCR5380 out of DMA mode, we 
+	     * can guarantee that we won't handshake another extra 
+	     * byte.
+    	     */
+
+	    while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
+	    /* Wait for clean handshake */
+	    while (NCR5380_read(STATUS_REG) & SR_REQ);
+	    d[c - 1] = NCR5380_read(INPUT_DATA_REG);
+	}
+#else
+	if (!(foo = NCR5380_pread(instance, d, c - 2))) {
+	    while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
+	    while (NCR5380_read(STATUS_REG) & SR_REQ);
+	    d[c - 2] = NCR5380_read(INPUT_DATA_REG);
+	    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+	    while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
+	    NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ACK);
+	    while (NCR5380_read(STATUS_REG) & SR_REQ);
+	    d[c - 2] = NCR5380_read(INPUT_DATA_REG);
+	}
+#endif
+    } else {
+	int timeout;
+	if (!(foo = NCR5380_pwrite(instance, d, c))) {
+	    /*
+	     * Wait for the last byte to be sent.  If REQ is being asserted for 
+	     * the byte we're interested, we'll ACK it and it will go false.  
+	     */
+	    if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
+		timeout = 20000;
+#if 1
+#if 1
+		while (!(NCR5380_read(BUS_AND_STATUS_REG) & 
+			BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) &
+		        BASR_PHASE_MATCH));
+#else
+		if (NCR5380_read(STATUS_REG) & SR_REQ) {
+		    for (; timeout && 
+			!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK); 
+			--timeout);
+		    for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ);
+			--timeout);
+		} 
+#endif
+	
+
+#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
+		if (!timeout) 
+		    printk("scsi%d: timed out on last byte\n",
+			    instance->host_no);
+#endif
+
+
+		if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
+		    hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
+		    if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
+			hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
+#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
+			printk("scsi%d: last bit sent works\n", 
+			    instance->host_no);
+#endif
+		    }
+		}
+	    } else 
+		while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
+#else
+	    udelay (5);
+#endif
+	}
+    }
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+    NCR5380_write(MODE_REG, MR_BASE);
+
+    *data = d + c;
+    *count = 0;
+    *phase = (NCR5380_read(STATUS_REG & PHASE_MASK));
+#if defined(PSEUDO_DMA) && !defined(UNSAFE)
+    sti();
+#endif /* defined(REAL_DMA_POLL) */
+    return foo;
+#endif /* def REAL_DMA */
+}
+#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target 
+ * 	directs us to.  Operates on the currently connected command, 
+ *	instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be 
+ *	modified if a command disconnects, *instance->connected will
+ *	change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here 
+ * 	to recover from an unexpected bus free condition.
+ */
+ 
+static void NCR5380_information_transfer (struct Scsi_Host *instance) {
+    NCR5380_local_declare();
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
+	instance->hostdata;
+    unsigned char msgout = NOP;
+    int sink = 0;
+    int len;
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+    int transfersize;
+#endif
+    unsigned char *data;
+    unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
+    Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+    NCR5380_setup(instance);
+
+    while (1) {
+	tmp = NCR5380_read(STATUS_REG);
+	/* We only have a valid SCSI phase when REQ is asserted */
+	if (tmp & SR_REQ) {
+	    phase = (tmp & PHASE_MASK); 
+	    if (phase != old_phase) {
+		old_phase = phase;
+#if (NDEBUG & NDEBUG_INFORMATION)
+		NCR5380_print_phase(instance);
+#endif
+	    }
+	    
+	    if (sink && (phase != PHASE_MSGOUT)) {
+		NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | 
+		    ICR_ASSERT_ACK);
+		while (NCR5380_read(STATUS_REG) & SR_REQ);
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+		    ICR_ASSERT_ATN);
+		sink = 0;
+		continue;
+	    }
+
+	    switch (phase) {
+	    case PHASE_DATAIN:
+	    case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+		printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n",
+		    instance->host_no);
+		sink = 1;
+		msgout = ABORT;
+		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+		break;
+#endif
+		/* 
+		 * If there is no room left in the current buffer in the
+		 * scatter-gather list, move onto the next one.
+		 */
+
+		if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+		    ++cmd->SCp.buffer;
+		    --cmd->SCp.buffers_residual;
+		    cmd->SCp.this_residual = cmd->SCp.buffer->length;
+		    cmd->SCp.ptr = cmd->SCp.buffer->address;
+#if (NDEBUG & NDEBUG_INFORMATION)
+		    printk("scsi%d: %d bytes and %d buffers left\n",
+			instance->host_no, cmd->SCp.this_residual,
+			cmd->SCp.buffers_residual);
+#endif
+		}
+
+		/*
+		 * The preferred transfer method is going to be 
+		 * PSEUDO-DMA for systems that are strictly PIO,
+		 * since we can let the hardware do the handshaking.
+		 *
+		 * For this to work, we need to know the transfersize
+		 * ahead of time, since the pseudo-DMA code will sit
+		 * in an unconditional loop.
+		 */
+
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+#ifdef NCR5380_dma_xfer_len
+		if (!cmd->device->borken &&
+		    (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
+#else
+                transfersize = cmd->transfersize;
+
+#ifdef LIMIT_TRANSFERSIZE  /* If we have problems with interrupt service */
+                if( transfersize > 512 )
+                    transfersize = 512;
+#endif  /* LIMIT_TRANSFERSIZE */
+
+                if (!cmd->device->borken && transfersize && 
+                    cmd->SCp.this_residual && !(cmd->SCp.this_residual % 
+                    transfersize)) {
+#endif
+		    len = transfersize;
+		    if (NCR5380_transfer_dma(instance, &phase,
+			&len, (unsigned char **) &cmd->SCp.ptr)) {
+			/*
+			 * If the watchdog timer fires, all future accesses to this
+			 * device will use the polled-IO.
+			 */ 
+			printk("scsi%d: switching target %d lun %d to slow handshake\n",
+			    instance->host_no, cmd->target, cmd->lun);
+			cmd->device->borken = 1;
+			NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+			    ICR_ASSERT_ATN);
+			sink = 1;
+			msgout = ABORT;
+			/* XXX - need to source or sink data here, as appropriate */
+		    } else
+			cmd->SCp.this_residual -= transfersize - len;
+		} else
+#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
+		  NCR5380_transfer_pio(instance, &phase, 
+		    (int *) &cmd->SCp.this_residual, (unsigned char **)
+		    &cmd->SCp.ptr);
+		break;
+	    case PHASE_MSGIN:
+		len = 1;
+		data = &tmp;
+		NCR5380_transfer_pio(instance, &phase, &len, &data);
+		cmd->SCp.Message = tmp;
+
+		switch (tmp) {
+		/*
+		 * Linking lets us reduce the time required to get the 
+		 * next command out to the device, hopefully this will
+		 * mean we don't waste another revolution due to the delays
+		 * required by ARBITRATION and another SELECTION.
+		 *
+		 * In the current implementation proposal, low level drivers
+		 * merely have to start the next command, pointed to by 
+		 * next_link, done() is called as with unlinked commands.
+		 */
+#ifdef LINKED
+		case LINKED_CMD_COMPLETE:
+		case LINKED_FLG_CMD_COMPLETE:
+		    /* Accept message by clearing ACK */
+		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+		    
+#if (NDEBUG & NDEBUG_LINKED) 
+		    printk("scsi%d: target %d lun %d linked command complete.\n",
+			instance->host_no, cmd->target, cmd->lun);
+#endif
+		    /* 
+		     * Sanity check : A linked command should only terminate with
+		     * one of these messages if there are more linked commands
+		     * available.
+		     */
+
+		    if (!cmd->next_link) {
+			 printk("scsi%d: target %d lun %d linked command complete, no next_link\n"
+			    instance->host_no, cmd->target, cmd->lun);
+			    sink = 1;
+			    msgout = ABORT;
+			    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+				ICR_ASSERT_ATN);
+			    break;
+		    }
+
+		    initialize_SCp(cmd->next_link);
+		    /* The next command is still part of this process */
+		    cmd->next_link->tag = cmd->tag;
+		    cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+#if (NDEBUG & NDEBUG_LINKED) 
+		    printk("scsi%d: target %d lun %d linked request done, calling scsi_done().\n",
+			instance->host_no, cmd->target, cmd->lun);
+#endif
+		    cmd->scsi_done(cmd);
+		    cmd = hostdata->connected;
+		    break;
+#endif /* def LINKED */
+		case ABORT:
+		case COMMAND_COMPLETE: 
+		    /* Accept message by clearing ACK */
+		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+		    hostdata->connected = NULL;
+#if (NDEBUG & NDEBUG_QUEUES)
+		    printk("scsi%d: command for target %d, lun %d completed\n",
+			instance->host_no, cmd->target, cmd->lun);
+#endif
+		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+
+		    /* 
+		     * I'm not sure what the correct thing to do here is : 
+		     * 
+		     * If the command that just executed is NOT a request 
+		     * sense, the obvious thing to do is to set the result
+		     * code to the values of the stored parameters.
+		     * 
+		     * If it was a REQUEST SENSE command, we need some way 
+		     * to differentiate between the failure code of the original
+		     * and the failure code of the REQUEST sense - the obvious
+		     * case is success, where we fall through and leave the result
+		     * code unchanged.
+		     * 
+		     * The non-obvious place is where the REQUEST SENSE failed 
+		     */
+
+		    if (cmd->cmnd[0] != REQUEST_SENSE) 
+			cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+		    else if (cmd->SCp.Status != GOOD)
+			cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+		    
+#ifdef AUTOSENSE
+		    if ((cmd->cmnd[0] != REQUEST_SENSE) && 
+			(cmd->SCp.Status == CHECK_CONDITION)) {
+#if (NDEBUG & NDEBUG_AUTOSENSE) 
+			printk("scsi%d: performing request sense\n", 
+			    instance->host_no);
+#endif
+			cmd->cmnd[0] = REQUEST_SENSE;
+			cmd->cmnd[1] &= 0xe0;
+			cmd->cmnd[2] = 0;
+			cmd->cmnd[3] = 0;
+			cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+			cmd->cmnd[5] = 0;
+
+			cmd->SCp.buffer = NULL;
+			cmd->SCp.buffers_residual = 0;
+			cmd->SCp.ptr = (char *) cmd->sense_buffer;
+			cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+
+			cli();
+			cmd->host_scribble = (unsigned char *) 
+			    hostdata->issue_queue;
+		        hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+		        sti();
+#if (NDEBUG & NDEBUG_QUEUES)
+			printk("scsi%d: REQUEST SENSE added to head of issue queue\n");
+#endif
+		   } else
+#endif /* def AUTOSENSE */
+			cmd->scsi_done(cmd);
+
+		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+		    /* 
+		     * Restore phase bits to 0 so an interrupted selection, 
+		     * arbitration can resume.
+		     */
+		    NCR5380_write(TARGET_COMMAND_REG, 0);
+		    
+		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && 
+			!hostdata->connected);
+		    return;
+		case MESSAGE_REJECT:
+		    /* Accept message by clearing ACK */
+		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+		    switch (hostdata->last_message) {
+		    case HEAD_OF_QUEUE_TAG:
+		    case ORDERED_QUEUE_TAG:
+		    case SIMPLE_QUEUE_TAG:
+			cmd->device->tagged_queue = 0;
+			hostdata->busy[cmd->target] |= (1 << cmd->lun);
+			break;
+		    default:
+			break;
+		    }
+		case DISCONNECT:
+		    /* Accept message by clearing ACK */
+		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+		    cmd->device->disconnect = 1;
+		    cli();
+		    cmd->host_scribble = (unsigned char *) 
+			hostdata->disconnected_queue;
+		    hostdata->connected = NULL;
+		    hostdata->disconnected_queue = cmd;
+		    sti();
+#if (NDEBUG & NDEBUG_QUEUES)
+		    printk("scsi%d: command for target %d lun %d was moved from connected to"
+		           "  the disconnected_queue\n", instance->host_no, 
+			    cmd->target, cmd->lun);
+#endif
+		    /* 
+		     * Restore phase bits to 0 so an interrupted selection, 
+		     * arbitration can resume.
+		     */
+		    NCR5380_write(TARGET_COMMAND_REG, 0);
+ 
+		    /* Enable reselect interrupts */
+		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+		    /* Wait for bus free to avoid nasty timeouts */
+		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && 
+			!hostdata->connected);
+		    return;
+		/* 
+		 * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+		 * operation, in violation of the SCSI spec so we can safely 
+		 * ignore SAVE/RESTORE pointers calls.
+		 *
+		 * Unfortunately, some disks violate the SCSI spec and 
+		 * don't issue the required SAVE_POINTERS message before
+		 * disconnecting, and we have to break spec to remain 
+		 * compatible.
+		 */
+		case SAVE_POINTERS:
+		case RESTORE_POINTERS:
+		    /* Accept message by clearing ACK */
+		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+		    break;
+		case EXTENDED_MESSAGE:
+/* 
+ * Extended messages are sent in the following format :
+ * Byte 	
+ * 0		EXTENDED_MESSAGE == 1
+ * 1		length (includes one byte for code, doesn't 
+ *		include first two bytes)
+ * 2 		code
+ * 3..length+1	arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.  
+ */
+		    extended_msg[0] = EXTENDED_MESSAGE;
+		    /* Accept first byte by clearing ACK */
+		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+		    printk("scsi%d: receiving extended message\n",
+			instance->host_no);
+#endif
+
+		    len = 2;
+		    data = extended_msg + 1;
+		    phase = PHASE_MSGIN;
+		    NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+		    printk("scsi%d: length=%d, code=0x%02x\n", 
+			instance->host_no, (int) extended_msg[1],
+			(int) extended_msg[2]);
+#endif
+
+		    if (!len && extended_msg[1] <= 
+			(sizeof (extended_msg) - 1)) {
+			/* Accept third byte by clearing ACK */
+			NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+			len = extended_msg[1] - 1;
+			data = extended_msg + 3;
+			phase = PHASE_MSGIN;
+
+			NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+		    printk("scsi%d: message received, residual %d\n",
+			instance->host_no, len);
+#endif
+
+			switch (extended_msg[2]) {
+			case EXTENDED_SDTR:
+			case EXTENDED_WDTR:
+			case EXTENDED_MODIFY_DATA_POINTER:
+			case EXTENDED_EXTENDED_IDENTIFY:
+			    tmp = 0;
+			}
+		    } else if (len) {
+			printk("scsi%d: error receiving extended message\n",
+			    instance->host_no);
+			tmp = 0;
+		    } else {
+			printk("scsi%d: extended message code %02x length %d is too long\n",
+			    instance->host_no, extended_msg[2], extended_msg[1]);
+			tmp = 0;
+		    }
+		/* Fall through to reject message */
+		    
+		/* 
+  		 * If we get something weird that we aren't expecting, 
+ 		 * reject it.
+		 */
+		default:
+		    if (!tmp) {
+			printk("scsi%d: rejecting message ", instance->host_no);
+			print_msg (extended_msg);
+			printk("\n");
+		    } else if (tmp != EXTENDED_MESSAGE)
+			printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n",
+			    instance->host_no, tmp, cmd->target, cmd->lun);
+		    else
+			printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n",
+			    instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun);
+   
+		    msgout = MESSAGE_REJECT;
+		    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+			ICR_ASSERT_ATN);
+		    break;
+		} /* switch (tmp) */
+		break;
+	    case PHASE_MSGOUT:
+		len = 1;
+		data = &msgout;
+		hostdata->last_message = msgout;
+		NCR5380_transfer_pio(instance, &phase, &len, &data);
+		if (msgout == ABORT) {
+		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+		    hostdata->connected = NULL;
+		    cmd->result = DID_ERROR << 16;
+		    cmd->scsi_done(cmd);
+		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+		    return;
+		}
+		msgout = NOP;
+		break;
+	    case PHASE_CMDOUT:
+		len = cmd->cmd_len;
+		data = cmd->cmnd;
+		/* 
+		 * XXX for performance reasons, on machines with a 
+		 * PSEUDO-DMA architecture we should probably 
+		 * use the dma transfer function.  
+		 */
+		NCR5380_transfer_pio(instance, &phase, &len, 
+		    &data);
+#ifdef USLEEP
+		if (!disconnect && should_disconnect(cmd->cmnd[0])) {
+		    hostdata->time_expires = jiffies + USLEEP_SLEEP;
+#if (NDEBUG & NDEBUG_USLEEP)
+		printk("scsi%d: issued command, sleeping until %ul\n", instance->host_no,
+		    hostdata->time_expires);
+#endif
+		    NCR5380_set_timer (instance);
+		    return;
+		}
+#endif /* def USLEEP */
+		break;
+	    case PHASE_STATIN:
+		len = 1;
+		data = &tmp;
+		NCR5380_transfer_pio(instance, &phase, &len, &data);
+		cmd->SCp.Status = tmp;
+		break;
+	    default:
+		printk("scsi%d: unknown phase\n", instance->host_no);
+#ifdef NDEBUG
+		NCR5380_print(instance);
+#endif
+	    } /* switch(phase) */
+	} /* if (tmp * SR_REQ) */ 
+#ifdef USLEEP
+	else {
+	    if (!disconnect && hostdata->time_expires && jiffies > 
+		hostdata->time_expires) {
+		hostdata->time_expires = jiffies + USLEEP_SLEEP;
+#if (NDEBUG & NDEBUG_USLEEP)
+		printk("scsi%d: poll timed out, sleeping until %ul\n", instance->host_no,
+		    hostdata->time_expires);
+#endif
+		NCR5380_set_timer (instance);
+		return;
+	    }
+	}
+#endif
+    } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected 
+ *	field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q 
+ *	nexus has been reestablished,
+ *	
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+
+static void NCR5380_reselect (struct Scsi_Host *instance) {
+    NCR5380_local_declare();
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+	instance->hostdata;
+    unsigned char target_mask;
+    unsigned char lun, phase;
+    int len;
+#ifdef SCSI2
+    unsigned char tag;
+#endif
+    unsigned char msg[3];
+    unsigned char *data;
+    Scsi_Cmnd *tmp = NULL, *prev;
+    int abort = 0;
+    NCR5380_setup(instance);
+
+    /*
+     * Disable arbitration, etc. since the host adapter obviously
+     * lost, and tell an interrupted NCR5380_select() to restart.
+     */
+
+    NCR5380_write(MODE_REG, MR_BASE);
+    hostdata->restart_select = 1;
+
+    target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+
+#if (NDEBUG & NDEBUG_RESELECTION)
+    printk("scsi%d: reselect\n", instance->host_no);
+#endif
+
+    /* 
+     * At this point, we have detected that our SCSI ID is on the bus,
+     * SEL is true and BSY was false for at least one bus settle delay
+     * (400 ns).
+     *
+     * We must assert BSY ourselves, until the target drops the SEL
+     * signal.
+     */
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+    
+    while (NCR5380_read(STATUS_REG) & SR_SEL);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    /*
+     * Wait for target to go into MSGIN.
+     */
+
+    while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+    len = 1;
+    data = msg;
+    phase = PHASE_MSGIN;
+    NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+
+    if (!msg[0] & 0x80) {
+	printk("scsi%d: expecting IDENTIFY message, got ",
+	    instance->host_no);
+	print_msg(msg);
+	abort = 1;
+    } else {
+	/* Accept message by clearing ACK */
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+	lun = (msg[0] & 0x07);
+
+	/* 
+	 * We need to add code for SCSI-II to track which devices have
+	 * I_T_L_Q nexuses established, and which have simple I_T_L
+	 * nexuses so we can chose to do additional data transfer.
+ 	 */
+
+#ifdef SCSI2
+#error "SCSI-II tagged queueing is not supported yet"
+#endif
+
+	/* 
+	 * Find the command corresponding to the I_T_L or I_T_L_Q  nexus we 
+	 * just reestablished, and remove it from the disconnected queue.
+	 */
+
+
+	for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; 
+	    tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) 
+	    if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
+#ifdef SCSI2
+	    && (tag == tmp->tag) 
+#endif
+) {
+	    if (prev)
+		prev->host_scribble = tmp->host_scribble;
+	    else
+		hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
+	    tmp->host_scribble = NULL;
+	    break;
+	}
+
+	if (!tmp) {
+#ifdef SCSI2
+	    printk("scsi%d: warning : target bitmask %02x lun %d tag %d not in disconnect_queue.\n",
+		    instance->host_no, target_mask, lun, tag);
+#else
+	    printk("scsi%d: warning : target bitmask %02x lun %d not in disconnect_queue.\n",
+		    instance->host_no, target_mask, lun);
+#endif
+	/* 
+	 * Since we have an established nexus that we can't do anything with,
+	 * we must abort it.  
+	 */
+	abort = 1;
+	}
+    }
+
+    if (abort) {
+	msg[0] = ABORT;
+	len = 1;
+	data = msg;
+	phase = PHASE_MSGOUT;
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+	NCR5380_transfer_pio(instance, &phase, &len, &data);
+    } else {
+	hostdata->connected = tmp;
+#if (NDEBUG & NDEBUG_RESELECTION)
+	printk"scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+	    instance->host_no, tmp->target, tmp->lun, tmp->tag);
+#endif
+    }
+}
+
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : called by interrupt handler when DMA finishes or a phase
+ *	mismatch occurs (which would finish the DMA transfer).  
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L
+ * 	nexus has been reestablished, on failure NULL is returned.
+ */
+
+#ifdef REAL_DMA
+static void NCR5380_dma_complete (NCR5380_instance *instance) {
+    NCR5380_local_declare();
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *
+	instance->hostdata);
+    int transferred;
+    NCR5380_setup(instance);
+    
+    /*
+     * XXX this might not be right.
+     *
+     * Wait for final byte to transfer, ie wait for ACK to go false.
+     *
+     * We should use the Last Byte Sent bit, unfortunately this is 
+     * not available on the 5380/5381 (only the various CMOS chips)
+     */
+
+    while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+
+    NCR5380_write(MODE_REG, MR_BASE);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    /*
+     * The only places we should see a phase mismatch and have to send
+     * data from the same set of pointers will be the data transfer
+     * phases.  So, residual, requested length are only important here.
+     */
+
+    if (!(hostdata->connected->SCp.phase & SR_CD)) {
+	transferred = instance->dmalen - NCR5380_dma_residual();
+	hostdata->connected->SCp.this_residual -= transferred;
+	hostdata->connected->SCp.ptr += transferred;
+    }
+}
+#endif /* def REAL_DMA */
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the 
+ * 	host byte of the result field to, if zero DID_ABORTED is 
+ *	used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently 
+ * 	 connected, you have to wait for it to complete.  If this is 
+ *	 a problem, we could implement longjmp() / setjmp(), setjmp()
+ * 	 called where the loop started in NCR5380_main().
+ */
+
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd) {
+    NCR5380_local_declare();
+    struct Scsi_Host *instance = cmd->host;
+    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+	instance->hostdata;
+    Scsi_Cmnd *tmp, **prev;
+    unsigned char msg, phase, *msgptr;
+    int len;
+
+    printk("scsi%d: aborting command\n", instance->host_no);
+    print_Scsi_Cmnd (cmd);
+
+    NCR5380_print_status (instance);
+
+    cli();
+    NCR5380_setup(instance);
+    
+#if (NDEBUG & NDEBUG_ABORT)
+    printk("scsi%d: abort called\n", instance->host_no);
+    printk("        basr 0x%X, sr 0x%X\n", 
+	   NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG));
+#endif
+
+#if 0
+/*
+ * Case 1 : If the command is the currently executing command, 
+ * we'll set the aborted flag and return control so that 
+ * information transfer routine can exit cleanly.
+ */
+
+    if (hostdata->connected == cmd) {
+#if (NDEBUG & NDEBUG_ABORT)
+	printk("scsi%d: aborting connected command\n", instance->host_no);
+#endif
+	hostdata->aborted = 1;
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+	NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN);
+/* 
+ * Since we can't change phases until we've completed the current 
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/* 
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */ 
+ 
+	return 0;
+    }
+#endif
+
+/* 
+ * Case 2 : If the command hasn't been issued yet, we simply remove it 
+ * 	    from the issue queue.
+ */
+    for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue), 
+	tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+	tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp = 
+	(Scsi_Cmnd *) tmp->host_scribble) 
+	if (cmd == tmp) {
+	    (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
+	    tmp->host_scribble = NULL;
+	    tmp->result = DID_ABORT << 16;
+	    sti();
+#if (NDEBUG & NDEBUG_ABORT)
+    printk("scsi%d: abort removed command from issue queue.\n", 
+	instance->host_no);
+#endif
+	    tmp->done(tmp);
+	    return SCSI_ABORT_SUCCESS;
+	}
+
+/* 
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ *	    and let the high level SCSI driver retry at a later time or 
+ *	    issue a reset.
+ *
+ *	    Timeouts, and therefore aborted commands, will be highly unlikely
+ *          and handling them cleanly in this situation would make the common
+ *	    case of noresets less efficient, and would pollute our code.  So,
+ *	    we fail.
+ */
+
+    if (hostdata->connected) {
+	sti();
+#if (NDEBUG & NDEBUG_ABORT)
+    printk("scsi%d: abort failed, command connected.\n", instance->host_no);
+#endif
+        return SCSI_ABORT_NOT_RUNNING;
+    }
+
+/*
+ * Case 4: If the command is currently disconnected from the bus, and 
+ * 	there are no connected commands, we reconnect the I_T_L or 
+ *	I_T_L_Q nexus associated with it, go into message out, and send 
+ *      an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select().  The easiest way to implement this 
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we 
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that 
+ * device reselected.
+ * 
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+    for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; 
+	tmp = (Scsi_Cmnd *) tmp->host_scribble) 
+        if (cmd == tmp) {
+            sti(); 
+#if (NDEBUG & NDEBUG_ABORT)
+    printk("scsi%d: aborting disconnected command.\n", instance->host_no);
+#endif
+  
+            if (NCR5380_select (instance, cmd, (int) cmd->tag)) 
+		return SCSI_ABORT_BUSY;
+
+#if (NDEBUG & NDEBUG_ABORT)
+    printk("scsi%d: nexus reestablished.\n", instance->host_no);
+#endif
+
+	    msg = ABORT;
+	    msgptr = &msg;
+	    len = 1;
+	    phase = PHASE_MSGOUT;
+	    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+	    NCR5380_transfer_pio (instance, &phase, &len, &msgptr);
+
+	    cli();
+	    for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue), 
+		tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
+		tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp = 
+		(Scsi_Cmnd *) tmp->host_scribble) 
+		    if (cmd == tmp) {
+		    *prev = (Scsi_Cmnd *) tmp->host_scribble;
+		    tmp->host_scribble = NULL;
+		    tmp->result = DID_ABORT << 16;
+		    sti();
+		    tmp->done(tmp);
+		    return SCSI_ABORT_SUCCESS;
+		}
+	}
+
+/*
+ * Case 5 : If we reached this point, the command was not found in any of 
+ *	    the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+    sti();
+    printk("scsi%d: warning : SCSI command probably completed successfully\n"
+           "        before abortion\n", instance->host_no); 
+    return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+/* 
+ * Function : int NCR5380_reset (Scsi_Cmnd *cmd)
+ * 
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SCSI_RESET_WAKEUP
+ *
+ */ 
+
+#ifndef NCR5380_reset
+static
+#endif
+int NCR5380_reset (Scsi_Cmnd *cmd) {
+    NCR5380_local_declare();
+    NCR5380_setup(cmd->host);
+
+    NCR5380_print_status (cmd->host);
+
+    cli();
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
+    udelay(25);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+    sti();
+
+    return SCSI_RESET_WAKEUP;
+}
+
diff -urNwbB linux/arch/arm/drivers/scsi/NCR5380.h linux.arm/arch/arm/drivers/scsi/NCR5380.h
--- linux/arch/arm/drivers/scsi/NCR5380.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/NCR5380.h	Sat Feb 24 09:36:54 1996
@@ -0,0 +1,352 @@
+/* 
+ * NCR 5380 defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix consulting and custom programming)
+ * 	drew@colorado.edu
+ *      +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6
+ *
+ * For more information, please consult 
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.h,v $
+ */
+
+#ifndef NCR5380_H
+#define NCR5380_H
+
+#define NCR5380_PUBLIC_RELEASE 6
+#ifdef NCR53C400
+#define NCR53C400_PUBLIC_RELEASE 1
+#endif
+
+#define NDEBUG_ARBITRATION	0x1
+#define NDEBUG_AUTOSENSE	0x2
+#define NDEBUG_DMA		0x4
+#define NDEBUG_HANDSHAKE	0x8
+#define NDEBUG_INFORMATION	0x10
+#define NDEBUG_INIT		0x20
+#define NDEBUG_INTR		0x40
+#define NDEBUG_LINKED		0x80
+#define NDEBUG_MAIN		0x100
+#define NDEBUG_NO_DATAOUT	0x200
+#define NDEBUG_NO_WRITE		0x400
+#define NDEBUG_PIO		0x800
+#define NDEBUG_PSEUDO_DMA	0x1000
+#define NDEBUG_QUEUES		0x2000
+#define NDEBUG_RESELECTION	0x4000
+#define NDEBUG_SELECTION	0x8000
+#define NDEBUG_USLEEP		0x10000
+#define NDEBUG_LAST_BYTE_SENT	0x20000
+#define NDEBUG_RESTART_SELECT	0x40000
+#define NDEBUG_EXTENDED		0x80000
+
+/* 
+ * The contents of the OUTPUT DATA register are asserted on the bus when
+ * either arbitration is occurring or the phase-indicating signals (
+ * IO, CD, MSG) in the TARGET COMMAND register and the ASSERT DATA
+ * bit in the INITIATOR COMMAND register is set.
+ */
+
+#define OUTPUT_DATA_REG         0       /* wo DATA lines on SCSI bus */
+#define CURRENT_SCSI_DATA_REG   0       /* ro same */
+
+#define INITIATOR_COMMAND_REG	1	/* rw */
+#define ICR_ASSERT_RST		0x80	/* rw Set to assert RST  */
+#define ICR_ARBITRATION_PROGRESS 0x40	/* ro Indicates arbitration complete */
+#define ICR_TRI_STATE		0x40	/* wo Set to tri-state drivers */
+#define ICR_ARBITRATION_LOST	0x20	/* ro Indicates arbitration lost */
+#define ICR_DIFF_ENABLE		0x20	/* wo Set to enable diff. drivers */
+#define ICR_ASSERT_ACK		0x10	/* rw ini Set to assert ACK */
+#define ICR_ASSERT_BSY		0x08	/* rw Set to assert BSY */
+#define ICR_ASSERT_SEL 		0x04	/* rw Set to assert SEL */
+#define ICR_ASSERT_ATN		0x02	/* rw Set to assert ATN */
+#define ICR_ASSERT_DATA		0x01	/* rw SCSI_DATA_REG is asserted */
+
+#ifdef DIFFERENTIAL
+#define ICR_BASE		ICR_DIFF_ENABLE
+#else
+#define ICR_BASE		0
+#endif
+
+#define MODE_REG		2
+/*
+ * Note : BLOCK_DMA code will keep DRQ asserted for the duration of the 
+ * transfer, causing the chip to hog the bus.  You probably don't want 
+ * this.
+ */
+#define MR_BLOCK_DMA_MODE	0x80	/* rw block mode DMA */
+#define MR_TARGET		0x40	/* rw target mode */
+#define MR_ENABLE_PAR_CHECK   0x20	/* rw enable parity checking */
+#define MR_ENABLE_PAR_INTR	0x10	/* rw enable bad parity interrupt */
+#define MR_ENABLE_EOP_INTR	0x08	/* rw enable eop interrupt */
+#define MR_MONITOR_BSY	0x04	/* rw enable int on unexpected bsy fail */
+#define MR_DMA_MODE		0x02	/* rw DMA / pseudo DMA mode */
+#define MR_ARBITRATE		0x01	/* rw start arbitration */
+
+#ifdef PARITY
+#define MR_BASE			MR_ENABLE_PAR_CHECK
+#else
+#define MR_BASE			0
+#endif
+
+#define TARGET_COMMAND_REG	3
+#define TCR_LAST_BYTE_SENT	0x80	/* ro DMA done */
+#define TCR_ASSERT_REQ		0x08	/* tgt rw assert REQ */
+#define TCR_ASSERT_MSG		0x04	/* tgt rw assert MSG */
+#define TCR_ASSERT_CD		0x02	/* tgt rw assert CD */
+#define TCR_ASSERT_IO		0x01	/* tgt rw assert IO */
+
+#define STATUS_REG		4	/* ro */
+/*
+ * Note : a set bit indicates an active signal, driven by us or another 
+ * device.
+ */
+#define SR_RST			0x80	
+#define SR_BSY			0x40
+#define SR_REQ			0x20
+#define SR_MSG			0x10
+#define SR_CD			0x08
+#define SR_IO			0x04
+#define SR_SEL			0x02
+#define SR_DBP			0x01
+
+/*
+ * Setting a bit in this register will cause an interrupt to be generated when 
+ * BSY is false and SEL true and this bit is asserted  on the bus.
+ */
+#define SELECT_ENABLE_REG	4	/* wo */
+
+#define BUS_AND_STATUS_REG	5	/* ro */
+#define BASR_END_DMA_TRANSFER	0x80	/* ro set on end of transfer */
+#define BASR_DRQ		0x40	/* ro mirror of DRQ pin */
+#define BASR_PARITY_ERROR	0x20	/* ro parity error detected */
+#define BASR_IRQ		0x10	/* ro mirror of IRQ pin */
+#define BASR_PHASE_MATCH	0x08	/* ro Set when MSG CD IO match TCR */
+#define BASR_BUSY_ERROR		0x04	/* ro Unexpected change to inactive state */
+#define BASR_ATN 		0x02	/* ro BUS status */
+#define BASR_ACK		0x01	/* ro BUS status */
+
+/* Write any value to this register to start a DMA send */
+#define START_DMA_SEND_REG	5	/* wo */
+
+/* 
+ * Used in DMA transfer mode, data is latched from the SCSI bus on
+ * the falling edge of REQ (ini) or ACK (tgt)
+ */
+#define INPUT_DATA_REG			6	/* ro */
+
+/* Write any value to this register to start a DMA receive */
+#define START_DMA_TARGET_RECEIVE_REG	6	/* wo */
+
+/* Read this register to clear interrupt conditions */
+#define RESET_PARITY_INTERRUPT_REG	7	/* ro */
+
+/* Write any value to this register to start an ini mode DMA receive */
+#define START_DMA_INITIATOR_RECEIVE_REG 7	/* wo */
+
+#ifdef NCR53C400
+#define C400_CONTROL_STATUS_REG                -8      /* rw */
+
+#define CSR_RESET              0x80    /* wo  Resets 53c400 */
+#define CSR_53C80_REG          0x80    /* ro  5380 registers busy */
+#define CSR_TRANS_DIR          0x40    /* rw  Data transfer direction */
+#define CSR_SCSI_BUFF_INTR     0x20    /* rw  Enable int on transfer ready */
+#define CSR_53C80_INTR         0x10    /* rw  Enable 53c80 interrupts */
+#define CSR_SHARED_INTR                0x08    /* rw  Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY   0x04    /* ro  Is Host buffer ready */
+#define CSR_SCSI_BUF_RDY       0x02    /* ro  SCSI buffer read */
+#define CSR_GATED_53C80_IRQ    0x01    /* ro  Last block xferred */
+
+#define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR
+
+/* Number of 128-byte blocks to be transferred */
+#define C400_CLOCK_COUNTER_REG         -7      /* rw */
+
+/* Resume transfer after disconnect */
+#define C400_RESUME_TRANSFER_REG       -6      /* wo */
+
+/* Access to host buffer stack */
+#define C400_HOST_BUFFER                       -4      /* rw */
+
+#endif /* NCR53C400 */
+
+
+/* Note : PHASE_* macros are based on the values of the STATUS register */
+#define PHASE_MASK 	(SR_MSG | SR_CD | SR_IO)
+
+#define PHASE_DATAOUT		0
+#define PHASE_DATAIN		SR_IO
+#define PHASE_CMDOUT		SR_CD
+#define PHASE_STATIN		(SR_CD | SR_IO)
+#define PHASE_MSGOUT		(SR_MSG | SR_CD)
+#define PHASE_MSGIN		(SR_MSG | SR_CD | SR_IO)
+#define PHASE_UNKNOWN		0xff
+
+/* 
+ * Convert status register phase to something we can use to set phase in 
+ * the target register so we can get phase mismatch interrupts on DMA 
+ * transfers.
+ */
+ 
+#define PHASE_SR_TO_TCR(phase) ((phase) >> 2)	
+
+/*
+ * The internal should_disconnect() function returns these based on the 
+ * expected length of a disconnect if a device supports disconnect/
+ * reconnect.
+ */
+
+#define DISCONNECT_NONE		0
+#define DISCONNECT_TIME_TO_DATA	1
+#define DISCONNECT_LONG		2
+
+/* 
+ * These are "special" values for the tag parameter passed to NCR5380_select.
+ */
+
+#define TAG_NEXT	-1 	/* Use next free tag */
+#define TAG_NONE	-2	/* 
+				 * Establish I_T_L nexus instead of I_T_L_Q
+				 * even on SCSI-II devices.
+				 */
+
+/*
+ * These are "special" values for the irq and dma_channel fields of the 
+ * Scsi_Host structure
+ */
+
+#define IRQ_NONE	255
+#define DMA_NONE	255
+#define IRQ_AUTO	254
+#define DMA_AUTO	254
+
+#define FLAG_HAS_LAST_BYTE_SENT		1	/* NCR53c81 or better */
+#define FLAG_CHECK_LAST_BYTE_SENT	2	/* Only test once */
+#define FLAG_NCR53C400			4	/* NCR53c400 */
+
+#ifndef ASM
+struct NCR5380_hostdata {
+    NCR5380_implementation_fields;		/* implementation specific */
+    unsigned char id_mask, id_higher_mask;	/* 1 << id, all bits greater */
+    unsigned char targets_present;		/* targets we have connected
+						   to, so we can call a select
+						   failure a retryable condition */
+    volatile unsigned char busy[8];		/* index = target, bit = lun */
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
+    volatile int dma_len;			/* requested length of DMA */
+#endif
+    volatile unsigned char last_message;	/* last message OUT */
+    volatile Scsi_Cmnd *connected;		/* currently connected command */
+    volatile Scsi_Cmnd *issue_queue;		/* waiting to be issued */
+    volatile Scsi_Cmnd *disconnected_queue;	/* waiting for reconnect */
+    volatile int restart_select;		/* we have disconnected,
+						   used to restart 
+						   NCR5380_select() */
+    volatile unsigned aborted:1;		/* flag, says aborted */
+    int flags;
+#ifdef USLEEP
+    unsigned long time_expires;			/* in jiffies, set prior to sleeping */
+    struct Scsi_Host *next_timer;
+#endif
+};
+
+#ifdef __KERNEL__
+static struct Scsi_Host *first_instance;		/* linked list of 5380's */
+
+#if defined(AUTOPROBE_IRQ)
+static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible);
+#endif
+static void NCR5380_init (struct Scsi_Host *instance, int flags);
+static void NCR5380_information_transfer (struct Scsi_Host *instance);
+static void NCR5380_intr (int irq, struct pt_regs * regs);
+static void NCR5380_main (void);
+static void NCR5380_print_options (struct Scsi_Host *instance);
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd);
+#ifndef NCR5380_reset
+static
+#endif
+int NCR5380_reset (Scsi_Cmnd *cmd);
+#ifndef NCR5380_queue_command
+static 
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+
+
+static void NCR5380_reselect (struct Scsi_Host *instance);
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
+#if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
+static int NCR5380_transfer_dma (struct Scsi_Host *instance,
+        unsigned char *phase, int *count, unsigned char **data);
+#endif
+static int NCR5380_transfer_pio (struct Scsi_Host *instance,
+        unsigned char *phase, int *count, unsigned char **data);
+
+#if (defined(REAL_DMA) || defined(REAL_DMA_POLL)) && defined(i386)
+static __inline__ int NCR5380_i386_dma_setup (struct Scsi_Host *instance,
+	unsigned char *ptr, unsigned int count, unsigned char mode) {
+    unsigned limit;
+
+    if (instance->dma_channel <=3) {
+	if (count > 65536)
+	    count = 65536;
+	limit = 65536 - (((unsigned) ptr) & 0xFFFF);
+    } else {
+	if (count > 65536 * 2) 
+	    count = 65536 * 2;
+	limit = 65536* 2 - (((unsigned) ptr) & 0x1FFFF);
+    }
+
+    if (count > limit) count = limit;
+
+    if ((count & 1) || (((unsigned) ptr) & 1))
+	panic ("scsi%d : attempted unaligned DMA transfer\n", instance->host_no);
+    cli();
+    disable_dma(instance->dma_channel);
+    clear_dma_ff(instance->dma_channel);
+    set_dma_addr(instance->dma_channel, (unsigned int) ptr);
+    set_dma_count(instance->dma_channel, count);
+    set_dma_mode(instance->dma_channel, mode);
+    enable_dma(instance->dma_channel);
+    sti();
+    return count;
+}
+
+static __inline__ int NCR5380_i386_dma_write_setup (struct Scsi_Host *instance,
+    unsigned char *src, unsigned int count) {
+    return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_WRITE);
+}
+
+static __inline__ int NCR5380_i386_dma_read_setup (struct Scsi_Host *instance,
+    unsigned char *src, unsigned int count) {
+    return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_READ);
+}
+
+static __inline__ int NCR5380_i386_dma_residual (struct Scsi_Host *instance) {
+    register int tmp;
+    cli();
+    clear_dma_ff(instance->dma_channel);
+    tmp = get_dma_residue(instance->dma_channel);
+    sti();
+    return tmp;
+}
+#endif /* defined(REAL_DMA) && defined(i386)  */
+#endif __KERNEL_
+#endif /* ndef ASM */
+#endif /* NCR5380_H */
diff -urNwbB linux/arch/arm/drivers/scsi/acornscsi-io.S linux.arm/arch/arm/drivers/scsi/acornscsi-io.S
--- linux/arch/arm/drivers/scsi/acornscsi-io.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/acornscsi-io.S	Sun Feb 11 09:32:30 1996
@@ -0,0 +1,253 @@
+@  linux/arch/arm/drivers/scsi/acornscsi-io.S: Acorn SCSI card IO
+@
+@ There is a reason for not using LDMs in this file - it kills the
+@ floppy drive and the serial port!  In order to keep those running
+@ error free, we use single word operations.
+@
+
+@ Purpose: transfer a block of data from the acorn scsi card to memory
+@ Proto  : void acornscsi_in(int portstrt, char *buffer, int length)
+@ Returns: nothing
+
+		.align
+		.global	___acornscsi_in
+___acornscsi_in:
+		mov	ip, sp
+		stmfd	sp!, {r4 - r10, fp, ip, lr, pc}
+		sub	fp, ip, #4
+		cmp	r0,#0x00C00000
+		movge	r3, #0
+		movlt	r3, #0x03000000
+		orrlt	r3, r3, #0x00010000
+		add	r0, r3, r0, lsl #2
+		mov	ip, #0xff
+		orr	ip, ip, ip, lsl #8
+acornscsi_in1lp:
+		subs	r2, r2, #32
+		bmi	acornscsi_in16
+		ldr	r3, [r0], #4
+		and	r3, r3, ip
+		ldr	r4, [r0], #4
+		orr	r3, r3, r4, lsl #16
+		ldr	r5, [r0], #4
+		and 	r4, r5, ip
+		ldr	r6, [r0], #4
+		orr	r4, r4, r6, lsl #16
+		ldr	r7, [r0], #4
+		and	r5, r7, ip
+		ldr	r8, [r0], #4
+		orr	r5, r5, r8, lsl #16
+		ldr	r9, [r0], #4
+		and	r6, r9, ip
+		ldr	r10, [r0], #4
+		orr	r6, r6, r10, lsl #16
+		stmia	r1!, {r3 - r6}
+		ldr	r3, [r0], #4
+		and	r3, r3, ip
+		ldr	r4, [r0], #4
+		orr	r3, r3, r4, lsl #16
+		ldr	r5, [r0], #4
+		and 	r4, r5, ip
+		ldr	r6, [r0], #4
+		orr	r4, r4, r6, lsl #16
+		ldr	r7, [r0], #4
+		and	r5, r7, ip
+		ldr	r8, [r0], #4
+		orr	r5, r5, r8, lsl #16
+		ldr	r9, [r0], #4
+		and	r6, r9, ip
+		ldr	r10, [r0], #4
+		orr	r6, r6, r10, lsl #16
+		stmia	r1!, {r3 - r6}
+		bne	acornscsi_in1lp
+		ldmea	fp, {r4 - r10, fp, sp, pc}^
+acornscsi_in16:	adds	r2, r2, #16
+		bmi	acornscsi_in8
+acornscsi_in16lp:
+		ldr	r3, [r0], #4
+		and	r3, r3, ip
+		ldr	r4, [r0], #4
+		orr	r3, r3, r4, lsl #16
+		ldr	r5, [r0], #4
+		and 	r4, r5, ip
+		ldr	r6, [r0], #4
+		orr	r4, r4, r6, lsl #16
+		ldr	r7, [r0], #4
+		and	r5, r7, ip
+		ldr	r8, [r0], #4
+		orr	r5, r5, r8, lsl #16
+		ldr	r9, [r0], #4
+		and	r6, r9, ip
+		ldr	r10, [r0], #4
+		orr	r6, r6, r10, lsl #16
+		stmia	r1!, {r3 - r6}
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		sub	r2, r2, #16
+acornscsi_in8:	adds	r2, r2, #8
+		bmi	acornscsi_in4
+		ldr	r3, [r0], #4
+		and	r3, r3, ip
+		ldr	r4, [r0], #4
+		orr	r3, r3, r4, lsl #16
+		ldr	r5, [r0], #4
+		and	r4, r5, ip
+		ldr	r6, [r0], #4
+		orr	r4, r4, r6, lsl #16
+		stmia	r1!, {r3 - r4}
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		sub	r2, r2, #8
+acornscsi_in4:	adds	r2, r2, #4
+		bmi	acornscsi_in2
+		ldr	r3, [r0], #4
+		and	r3, r3, ip
+		ldr	r4, [r0], #4
+		orr	r3, r3, r4, lsl #16
+		str	r3, [r1], #4
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		sub	r2, r2, #4
+acornscsi_in2:	adds	r2, r2, #2
+		ldr	r3, [r0], #4
+		and	r3, r3, ip
+		strb	r3, [r1], #1
+		mov	r3, r3, lsr #8
+		strplb	r3, [r1], #1
+		ldmea	fp, {r4 - r10, fp, sp, pc}^
+
+@ Purpose: transfer a block of data from memory to the acorn scsi card
+@ Proto  : void acornscsi_in(int portstrt, char *buffer, int length)
+@ Returns: nothing
+
+		.global	___acornscsi_out
+___acornscsi_out:
+		mov	ip, sp
+		stmfd	sp!, {r4 - r10, fp, ip, lr, pc}
+		sub	fp, ip, #4
+		cmp	r0,#0x00C00000
+		movge	r3, #0
+		movlt	r3, #0x03000000
+		orrlt	r3, r3, #0x00010000
+		add	r0, r3, r0, lsl #2
+		mov	ip, #0xff
+		orr	ip, ip, ip, lsl #8
+acornscsi_out1lp:
+		subs	r2, r2, #32
+		bmi	acornscsi_out16
+		ldmia	r1!, {r7 - r10}
+		mov	r3, r7, lsl #16
+		orr	r3, r3, r3, lsr #16
+		str	r3, [r0], #4
+		mov	r4, r7, lsr #16
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r0], #4
+		mov	r5, r8, lsl #16
+		orr	r5, r5, r5, lsr #16
+		str	r5, [r0], #4
+		mov	r6, r8, lsr #16
+		orr	r6, r6, r6, lsl #16
+		str	r6, [r0], #4
+		mov	r7, r9, lsl #16
+		orr	r7, r7, r7, lsr #16
+		str	r7, [r0], #4
+		mov	r8, r9, lsr #16
+		orr	r8, r8, r8, lsl #16
+		str	r8, [r0], #4
+		mov	r9, r10, lsl #16
+		orr	r9, r9, r9, lsr #16
+		str	r9, [r0], #4
+		mov	r10, r10, lsr #16
+		orr	r10, r10, r10, lsl #16
+		str	r10, [r0], #4
+		ldmia	r1!, {r7 - r10}
+		mov	r3, r7, lsl #16
+		orr	r3, r3, r3, lsr #16
+		str	r3, [r0], #4
+		mov	r4, r7, lsr #16
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r0], #4
+		mov	r5, r8, lsl #16
+		orr	r5, r5, r5, lsr #16
+		str	r5, [r0], #4
+		mov	r6, r8, lsr #16
+		orr	r6, r6, r6, lsl #16
+		str	r6, [r0], #4
+		mov	r7, r9, lsl #16
+		orr	r7, r7, r7, lsr #16
+		str	r7, [r0], #4
+		mov	r8, r9, lsr #16
+		orr	r8, r8, r8, lsl #16
+		str	r8, [r0], #4
+		mov	r9, r10, lsl #16
+		orr	r9, r9, r9, lsr #16
+		str	r9, [r0], #4
+		mov	r10, r10, lsr #16
+		orr	r10, r10, r10, lsl #16
+		str	r10, [r0], #4
+		bne	acornscsi_out1lp
+		ldmea	fp, {r4 - r10, fp, sp, pc}^
+acornscsi_out16:
+		adds	r2, r2, #16
+		bmi	acornscsi_out8
+acornscsi_out16lp:
+		ldmia	r1!, {r7 - r10}
+		mov	r3, r7, lsl #16
+		orr	r3, r3, r3, lsr #16
+		str	r3, [r0], #4
+		mov	r4, r7, lsr #16
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r0], #4
+		mov	r5, r8, lsl #16
+		orr	r5, r5, r5, lsr #16
+		str	r5, [r0], #4
+		mov	r6, r8, lsr #16
+		orr	r6, r6, r6, lsl #16
+		str	r6, [r0], #4
+		mov	r7, r9, lsl #16
+		orr	r7, r7, r7, lsr #16
+		str	r7, [r0], #4
+		mov	r8, r9, lsr #16
+		orr	r8, r8, r8, lsl #16
+		str	r8, [r0], #4
+		mov	r9, r10, lsl #16
+		orr	r9, r9, r9, lsr #16
+		str	r9, [r0], #4
+		mov	r10, r10, lsr #16
+		orr	r10, r10, r10, lsl #16
+		str	r10, [r0], #4
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		sub	r2, r2, #16
+acornscsi_out8:	adds	r2, r2, #8
+		bmi	acornscsi_out4
+		ldmia	r1!, {r5 - r6}
+		mov	r3, r5, lsl #16
+		orr	r3, r3, r3, lsr #16
+		str	r3, [r0], #4
+		mov	r4, r5, lsr #16
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r0], #4
+		mov	r5, r6, lsl #16
+		orr	r5, r5, r5, lsr #16
+		str	r5, [r0], #4
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r6, lsl #16
+		str	r6, [r0], #4
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		sub	r2, r2, #8
+acornscsi_out4:	adds	r2, r2, #4
+		bmi	acornscsi_out2
+		ldr	r4, [r1], #4
+		mov	r3, r4, lsl #16
+		orr	r3, r3, r3, lsr #16
+		str	r3, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r4, lsl #16
+		str	r4, [r0], #4
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		sub	r2, r2, #4
+acornscsi_out2:	adds	r2, r2, #2
+		ldr	r3, [r1], #2
+		and	r3, r3, ip
+		strb	r3, [r0], #1
+		mov	r3, r3, lsr #8
+		strplb	r3, [r0], #1
+		ldmea	fp, {r4 - r10, fp, sp, pc}^
+
diff -urNwbB linux/arch/arm/drivers/scsi/acornscsi.c linux.arm/arch/arm/drivers/scsi/acornscsi.c
--- linux/arch/arm/drivers/scsi/acornscsi.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/acornscsi.c	Sun Feb 11 09:32:31 1996
@@ -0,0 +1,1967 @@
+/*
+ * linux/arch/arm/drivers/scsi/acornscsi.c
+ *
+ *  Acorn scsi SP3 driver
+ *
+ *  By R.M.King, using SP3 info supplied by Acorn.
+ *
+ * Abandoned using the Select and Transfer command since there were
+ * some nasty races between our software and the target devices that
+ * were not easy to solve, and the device specs said that there were
+ * some nasty bugs in that command...
+ *
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/config.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/ecard.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "acornscsi.h"
+#include "constants.h"
+
+#define DEBUG_NO_WRITE	1
+#define DEBUG_QUEUES	2
+#define DEBUG_DMA	4
+#define DEBUG_ABORT	8
+#define DEBUG_DISCON	16
+#define DEBUG_CONNECT	32
+#define DEBUG_PHASES	64
+#define DEBUG_WRITE	128
+#define DEBUG_LINK	256
+
+/*
+ *====================================================================================
+ *
+ * Driver Configuration
+ *
+ *
+ *
+ * SCSI-II Tagged queue support.
+ *
+ * I don't have any SCSI devices that support it, so it is totally untested (except to
+ * make sure that it doesn't interfere with any non-tagging devices).  It is not fully
+ * implemented either - what happens when a tagging device reconnects???
+ */
+#undef SCSI2_TAG
+
+/*
+ * SCSI-II Linked command support.
+ *
+ * The higher level code doesn't support linked commands yet.
+ */
+#undef SCSI2_LINK
+
+/*
+ * Debugging information
+ *
+ * Please see debug list above
+ */
+#define DEBUG (DEBUG_ABORT)
+
+/*
+ * List of devices that the driver will recognise
+ *
+ * PRODS is the list of product numbers
+ * MANUS is the list of manufacturer numbers
+ *
+ * NOTE: this is used as a pair of numbers.  If you add a number in the middle of
+ *       one, then you must add a number in at the same place in the other.
+ */
+#define PRODS 0x0002
+#define MANUS 0x0000
+/*
+ *====================================================================================
+ */
+
+#define VER_MAJOR 1
+#define VER_MINOR 4
+
+#if 0
+/*
+ * This is as per acorns spec.
+ */
+#define INIT_DEVCON0 (DEVCON0_RQL | DEVCON0_EXW)
+#define INIT_DEVCON1 (DEVCON1_BHLD)
+#define INIT_SBICDMA (CTRL_DMABURST)
+#define DMAC_READ    (MODECON_READ)
+#define DMAC_WRITE   (MODECON_WRITE)
+#else
+/*
+ * Hopefully this will improve performance with floppy disk + serial port
+ */
+#define INIT_DEVCON0 (DEVCON0_RQL | DEVCON0_EXW)
+#define INIT_DEVCON1 (0)
+#define INIT_SBICDMA (CTRL_DMAMODE)
+#define DMAC_READ    (MODECON_READ|MODECON_SINGLE)
+#define DMAC_WRITE   (MODECON_WRITE|MODECON_SINGLE)
+#endif
+
+#define POD_SPACE(x) ((x) + 0xd0000)
+#define SCpdirection SCp.phase
+#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0)
+#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1)
+#define RESET_STATUS(x) ({ status_ptr = 0; })
+#define ADD_STATUS(x) ({ status_array[status_ptr] = (x); status_ptr = (status_ptr + 1) & 63; })
+
+static unsigned char status_array[64];
+static unsigned int status_ptr = 0;
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/*
+ * Todo:
+ *     Complete abort processing code when command is disconnected [waiting for one to happen]
+ */
+
+static struct proc_dir_entry proc_scsi_acornscsi = {
+	PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static inline void sbic_write (struct Scsi_Host *instance, int reg, int value)
+{
+    outb(reg, instance->io_port + 0x800);
+    outw(value, instance->io_port + 0x801);
+}
+
+static inline int sbic_read (struct Scsi_Host *instance, int reg)
+{
+    if(reg == ASR)
+    	   return inw(instance->io_port + 0x800) & 0xff;
+    outb(reg, instance->io_port + 0x800);
+    return inw(instance->io_port + 0x801) & 0xff;
+}
+
+static inline void page_write (struct Scsi_Host *instance, int value)
+{
+    outb(value, POD_SPACE(instance->io_port) + 0xc00);
+}
+
+static inline int dmac_read (struct Scsi_Host *instance, int reg)
+{
+    return inb (instance->io_port + 0xc00 + reg);
+}
+
+static inline void dmac_write (struct Scsi_Host *instance, int reg, int value)
+{
+    outb (value, instance->io_port + 0xc00 + reg);
+}
+
+static inline void dmac_clearintr (struct Scsi_Host *instance)
+{
+    outb (0, POD_SPACE(instance->io_port) + 0x800);
+}
+
+static inline int read_irqstat (struct Scsi_Host *instance)
+{
+    return inb (POD_SPACE(instance->io_port) + 0x800);
+}
+
+static inline void sbic_reset (struct Scsi_Host *instance)
+{
+    sbic_write (instance, OWNID, OWNID_EAF | instance->this_id);
+    sbic_write (instance, CMND, CMND_RESET);
+}
+
+/*=============================================================================================
+ * utility routines
+ */
+static void print_sbic_status (int asr, int ssr, int cmdphase)
+{
+    printk("sbic status: %s %s %s %s %s %s ",
+        asr & ASR_INT ? "INT" : "int",
+        asr & ASR_LCI ? "LCI" : "lci",
+        asr & ASR_BSY ? "BSY" : "bsy",
+        asr & ASR_CIP ? "CIP" : "cip",
+    	asr & ASR_PE  ? "PE"  : "pe",
+    	asr & ASR_DBR ? "DBR" : "dbr");
+    printk("scsi status: %X:%X phase %02X\n",
+        (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
+}
+
+static void acornscsi_printstatus (struct Scsi_Host *instance)
+{
+    int statptr;
+    printk ("IRQs: ");
+    statptr = status_ptr - 20;
+    if (statptr < 0)
+	statptr += 64;
+    for (; statptr != status_ptr; statptr = (statptr + 2) & 63) {
+    	printk ("[%02X:%02X] ", status_array[statptr], status_array[statptr+1]);
+    }
+    printk ("\n");
+}
+
+static inline int acornscsi_cmdtype (int command)
+{
+    switch (command) {
+    	case WRITE_6:
+    	case WRITE_10:
+    	case WRITE_12:
+    	    return CMD_WRITE;
+    	case READ_6:
+    	case READ_10:
+    	case READ_12:
+    	    return CMD_READ;
+    	default:
+    	    return CMD_MISC;
+    }
+}
+
+static int acornscsi_writedirection (int command)
+{
+    switch (command) {
+	case CHANGE_DEFINITION:
+	case COMPARE:
+	case COPY:
+	case COPY_VERIFY:
+	case LOG_SELECT:
+	case MODE_SELECT:
+	case MODE_SELECT_10:
+	case SEND_DIAGNOSTIC:
+	case WRITE_BUFFER:
+	case FORMAT_UNIT:
+	case REASSIGN_BLOCKS:
+	case RESERVE:
+	case SEARCH_EQUAL:
+	case SEARCH_HIGH:
+	case SEARCH_LOW:
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_VERIFY:
+	case UPDATE_BLOCK:
+	case WRITE_LONG:
+	case WRITE_SAME:
+	case SEARCH_HIGH_12:
+	case SEARCH_EQUAL_12:
+	case SEARCH_LOW_12:
+	case WRITE_12:
+	case WRITE_VERIFY_12:
+	case SET_WINDOW:
+	case MEDIUM_SCAN:
+	case SEND_VOLUME_TAG:
+	case 0xea:
+	    return 1;
+	case TEST_UNIT_READY:
+	default:
+	    return 0;
+    }
+}
+
+static inline void acornscsi_setupdmac(struct Scsi_Host *instance, int mode,
+		unsigned long address, int length)
+{
+    dmac_write (instance, MASKREG, MASK_ON);
+    if (length) {
+	length -= 1;
+	dmac_write (instance, TXCNTLO, length);
+	dmac_write (instance, TXCNTHI, length >> 8);
+	dmac_write (instance, TXADRLO, address);
+	dmac_write (instance, TXADRMD, address >> 8);
+	dmac_write (instance, TXADRHI, address >> 16);
+	dmac_write (instance, MODECON, mode);
+	dmac_write (instance, MASKREG, MASK_OFF);
+    } else {
+	dmac_write (instance, TXCNTLO, 0);
+	dmac_write (instance, TXCNTHI, 0);
+    }
+}
+
+/*
+ * Function : acornscsi_initialisebuffer (Scsi_Cmnd *cmd)
+ *
+ * Purpose  : Initialise the buffer pointers
+ *
+ * Params   : cmd - command structure
+ *
+ */
+static inline void acornscsi_initialisebuffer (Scsi_Cmnd *cmd)
+{
+    if (cmd->use_sg) {
+	cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+	cmd->SCp.buffers_residual = cmd->use_sg - 1;
+	cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+	cmd->SCp.this_residual = cmd->SCp.buffer->length;
+    } else {
+	cmd->SCp.buffer = NULL;
+	cmd->SCp.buffers_residual = 0;
+	cmd->SCp.ptr = (char *) cmd->request_buffer;
+	cmd->SCp.this_residual = cmd->request_bufflen;
+    }
+}
+
+/*
+ * Function : acornscsi_nextbuffer (Scsi_Cmnd *cmd)
+ *
+ * Purpose  : If the current buffer is empty, move onto the next buffer
+ *
+ * Params   : cmd - command structure containing the buffers.
+ *
+ * Returns  : zero if no more buffers, else non-zero
+ */
+static inline int acornscsi_nextbuffer (Scsi_Cmnd *cmd)
+{
+    if (cmd->SCp.this_residual == 0)
+	if (cmd->SCp.buffers_residual) {
+	    cmd->SCp.buffer++;
+	    cmd->SCp.buffers_residual--;
+	    cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+	    cmd->SCp.this_residual = cmd->SCp.buffer->length;
+	    return 1;
+	} else {
+	    cmd->SCp.ptr = NULL;
+	    cmd->SCp.this_residual = 0;
+	    return 0;
+	}
+}
+
+/*
+ * Function : acornscsi_readbuffer (struct Scsi_Host *instance, char *buffer,
+ *			unsigned long boardaddr, int len)
+ *
+ * Purpose  : transfer a block of data from the scsi card to memory
+ *
+ * Params   : instance  - host specific data
+ *          : buffer    - address to put data
+ *          : boardaddr - address of data on scsi card
+ *          : len       - length of data to transfer
+ *
+ */
+static void acornscsi_readtobh (struct Scsi_Host *instance, struct acornscsi_hostdata *hostdata,
+			struct as_cmnd *ascmd, short residual)
+{
+    unsigned long buffer_length;
+    unsigned int page, offset;
+    Scsi_Cmnd *SCpnt;
+    extern void __acornscsi_in (int port, char *buffer, int transfer);
+
+    SCpnt = ascmd->cmd;
+
+    if (ascmd->buffer) {
+    	page = (ascmd->buf1addr >> 12) & 0x3f;
+    	offset = ascmd->buf1addr & 4095;
+	buffer_length = ascmd->buf1len - residual;
+    } else {
+    	page = (ascmd->buf0addr >> 12) & 0x3f;
+    	offset = ascmd->buf0addr & 4095;
+	buffer_length = ascmd->buf0len - residual;
+    }
+
+    page_write (instance, hostdata->page_reg | page);
+
+#if (DEBUG & DEBUG_DMA)
+    printk ("scsi%d :  debug : readtobh: (%04lX %04lX)\n        ", instance->host_no, (page << 12) | offset,
+	buffer_length);
+#endif
+
+    while (buffer_length > 0) {
+    	int this_length;
+
+	if (SCpnt->SCp.this_residual > buffer_length)
+	    this_length = buffer_length;
+	else
+	    this_length = SCpnt->SCp.this_residual;
+
+	SCpnt->SCp.this_residual -= this_length;
+	buffer_length  -= this_length;
+
+#if (DEBUG & DEBUG_DMA)
+	printk ("[%p %04lX %04X] ", SCpnt->SCp.ptr, (page << 12) | offset, this_length);
+#endif
+
+	while (this_length > 0) {
+	    int this_trans;
+
+	    this_trans = 4096 - offset;
+	    if (this_trans > this_length)
+		this_trans = this_length;
+
+	    __acornscsi_in (instance->io_port + (offset >> 1), SCpnt->SCp.ptr, this_trans);
+
+	    SCpnt->SCp.ptr += this_trans;
+	    offset         += this_trans;
+	    this_length    -= this_trans;
+
+	    if (offset > 4095) {
+	        offset -= 4096;
+	        page    = (page + 1) & 0x3f;
+	        page_write (instance, hostdata->page_reg | page);
+	    }
+	}
+
+	if (acornscsi_nextbuffer (SCpnt) == 0)
+	    break;
+    }
+#if (DEBUG & DEBUG_DMA)
+    printk ("\n");
+#endif
+}
+
+/*
+ * Function : acornscsi_writebuffer (struct Scsi_Host *instance, char *buffer,
+ *			unsigned long boardaddr, int len)
+ *
+ * Purpose  : transfer a block of data from memory to the scsi card
+ *
+ * Params   : instance  - host specific data
+ *          : buffer    - address to put data
+ *          : boardaddr - address of data on scsi card
+ *          : len       - length of data to transfer
+ *
+ */
+static inline void acornscsi_writebuffer (struct Scsi_Host *instance, char *buffer,
+			unsigned long boardaddr, int len)
+{
+    extern void __acornscsi_out (int port, char *buffer, int transfer);
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    unsigned int page, offset;
+
+    page = (boardaddr >> 12) & 0x3f;
+    offset = boardaddr & 4095;
+
+    page_write (instance, hostdata->page_reg | page);
+
+    while (len > 0) {
+	int this_trans;
+
+	this_trans = 4096 - offset;
+	if (len < this_trans)
+	    this_trans = len;
+
+	__acornscsi_out (instance->io_port + (offset >> 1), buffer, this_trans);
+
+	buffer += this_trans;
+	offset += this_trans;
+	len    -= this_trans;
+
+	if (offset > 4095) {
+	    offset -= 4096;
+	    page    = (page + 1) & 0x3f;
+	    page_write (instance, hostdata->page_reg | page);
+	}
+    }
+}
+
+static unsigned long acornscsi_writefrombh (struct Scsi_Host *instance,	struct as_cmnd *ascmd,
+					int buffer)
+{
+    Scsi_Cmnd *SCpnt;
+    unsigned long buffer_address, buffer_offset;
+    SCpnt = ascmd->cmd;
+
+    if (buffer)
+	buffer_address = ascmd->buf1addr;
+    else
+	buffer_address = ascmd->buf0addr;
+
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+    printk ("scsi%d :  debug : writefrombh: (%04lX)\n        ", instance->host_no, buffer_address);
+#endif
+
+    buffer_offset = 0;
+
+    do {
+	int this_length;
+    
+	if (buffer_offset + SCpnt->SCp.this_residual >= 4096)
+	    this_length = 4096 - buffer_offset;
+	else
+	    this_length = SCpnt->SCp.this_residual;
+
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+	printk ("[%p %04lX %04X] ", SCpnt->SCp.ptr, buffer_address + buffer_offset, this_length);
+#endif
+
+	acornscsi_writebuffer (instance, SCpnt->SCp.ptr, buffer_address + buffer_offset, this_length);
+
+	SCpnt->SCp.this_residual -= this_length;
+	SCpnt->SCp.ptr += this_length;
+	buffer_offset += this_length;
+
+	if (acornscsi_nextbuffer (SCpnt) == 0)
+	    break;
+    } while (buffer_offset < 4096);
+
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+    printk ("\n");
+#endif
+    if (buffer)
+	ascmd->buf1len = buffer_offset;
+    else
+	ascmd->buf0len = buffer_offset;
+
+    return buffer_offset;
+}    
+/*
+ * Function : acornscsi_handletransfer (struct Scsi_Host *instance);
+ *
+ * Purpose  : transfers a block of data from the on-board buffer
+ *            to the scatter list or buffer and updates the dmac
+ *
+ * Params   : instance - the device to handle
+ *
+ */
+static inline int acornscsi_handletransfer (struct Scsi_Host *instance)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    struct as_cmnd *ascmd = hostdata->connected;
+    
+    if (!ascmd) {
+    	printk("scsi%d : warning: dmac interrupt with no connected command\n", instance->host_no);
+    	return 0;
+    }
+
+    dmac_write (instance, MASKREG, MASK_ON);
+    dmac_clearintr (instance);
+
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+    printk ("htrans: ");
+#endif
+    switch (hostdata->phase) {
+    case PHASE_COMMANDSENT:
+	printk ("\n");
+	return 0;
+
+    case PHASE_DATAIN:
+	if (ascmd->buffer) {
+	    ascmd->totallen -= ascmd->buf1len;
+	    if (ascmd->totallen > 4096)
+		ascmd->buf0len = 4096;
+	    else
+		ascmd->buf0len = ascmd->totallen;
+
+	    acornscsi_setupdmac (instance, DMAC_READ, ascmd->buf0addr, ascmd->buf0len);
+	} else {
+	    ascmd->totallen -= ascmd->buf0len;
+	    if (ascmd->totallen > 4096)
+		ascmd->buf1len = 4096;
+	    else
+		ascmd->buf1len = ascmd->totallen;
+
+	    acornscsi_setupdmac (instance, DMAC_READ, ascmd->buf1addr, ascmd->buf1len);
+	}
+	acornscsi_readtobh (instance, hostdata, ascmd, 0);
+	ascmd->buffer ^= 1;
+	if (ascmd->totallen == 0)
+	    return 0;
+	return 1;
+
+    case PHASE_DATAOUT:
+	if (ascmd->buffer) {
+	    ascmd->totallen -= ascmd->buf1len;
+
+	    acornscsi_setupdmac (instance, DMAC_WRITE, ascmd->buf0addr, ascmd->buf0len);
+	    ascmd->buffer = 0;
+
+	    acornscsi_writefrombh (instance, ascmd, 1);
+	} else {
+	    ascmd->totallen -= ascmd->buf0len;
+
+	    acornscsi_setupdmac (instance, DMAC_WRITE, ascmd->buf1addr, ascmd->buf1len);
+	    ascmd->buffer = 1;
+
+	    acornscsi_writefrombh (instance, ascmd, 0);
+	}
+
+	if (ascmd->totallen == 0)
+	    return 0;
+	return 1;
+    }
+    return 0;
+}
+
+static void acornscsi_sendcommand (struct Scsi_Host *instance,
+			struct acornscsi_hostdata *hostdata)
+{
+    struct as_dev *dev;
+    struct as_cmnd *asprev, *ascmd;
+    int i;
+
+    cli ();
+    if (hostdata->connected)
+	goto not_busy;
+
+    if (hostdata->aborting) {
+	if (!(sbic_read (instance, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+	    hostdata->phase = PHASE_CONNECTING_FOR_ABORT;
+	    sbic_write (instance, DESTID, hostdata->aborting->cmd->target);
+	    sbic_write (instance, CMND, CMND_SELWITHATN);
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_ABORT|DEBUG_CONNECT))
+	    printk ("scsi%d : restarting abortion of id %d\n", instance->host_no,
+	    		hostdata->aborting->cmd->target);
+#endif
+	}
+	goto not_busy;
+    }
+
+    if (hostdata->connecting) {
+	if (!(sbic_read (instance, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+	    hostdata->phase = PHASE_CONNECTING;
+	    sbic_write (instance, DESTID, hostdata->connecting->cmd->target);
+	    sbic_write (instance, CMND, CMND_SELWITHATN);
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
+	    printk ("scsi%d : restarting selection of id %d\n", instance->host_no,
+	    		hostdata->connecting->cmd->target);
+#endif
+	}
+	goto not_busy;
+    }
+
+    for (i = 0, dev = hostdata->dev; i < 8; i++, dev++) {
+	for (ascmd = dev->issue_head, asprev = NULL; ascmd; asprev = ascmd, ascmd = ascmd->next) {
+	    if (!ascmd->used || !ascmd->cmd || !ascmd->cmd->scsi_done) {
+	        printk ("scsi%d : warning: invalid %s found in issue queue\n",
+			instance->host_no, ascmd->used ? ascmd->cmd ? "scsi_done" : "cmd" : "used");
+		if (asprev)
+		    asprev = ascmd->next;
+		else
+		    dev->issue_head = ascmd->next;
+		if (ascmd == dev->issue_tail)
+		    dev->issue_tail = asprev;
+		continue;
+	    }
+
+	    if (ascmd->cmd->device->tagged_queue == 0) {
+		if (set_bit (ascmd->cmd->lun, &dev->busy))
+		    continue;
+	    }
+#ifdef SCSI2_TAG
+	    else {
+		/*
+		 * SCSI-II Tagged support.  If the command is not a request sense, then
+		 * we allocate a tag to it.
+		 */
+		if (ascmd->cmd->cmnd[0] != REQUEST_SENSE) {
+		    if (ascmd->cmd->device->current_tag == 0) {
+			printk ("scsi%d : Enabling tagged queueing for target %d.\n",
+			    ascmd->cmd->host->host_no, ascmd->cmd->target);
+			ascmd->cmd->device->current_tag = 1; /* enable tagging */
+		    }
+		    ascmd->cmd->tag = ascmd->cmd->device->current_tag++;
+		    if (ascmd->cmd->device->current_tag == 0) /* MUST not become zero */
+			ascmd->cmd->device->current_tag += 1;
+		}
+	    }
+#endif
+
+	    hostdata->connecting = ascmd;
+
+	    if (asprev)
+	        asprev->next = ascmd->next;
+	    else
+	        dev->issue_head = ascmd->next;
+
+	    if (ascmd == dev->issue_tail)
+	        dev->issue_tail = asprev;
+
+	    hostdata->stats.removes += 1;
+
+	    if (!(sbic_read (instance, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+		sti ();
+	    	hostdata->phase = PHASE_CONNECTING;
+		sbic_write (instance, DESTID, hostdata->connecting->cmd->target);
+		sbic_write (instance, CMND, CMND_SELWITHATN);
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
+		printk ("scsi%d :  debug : starting selection of id %d\n", instance->host_no,
+	    		hostdata->connecting->cmd->target);
+#endif
+	    }
+
+	    sti ();
+
+	    switch (acornscsi_cmdtype (ascmd->cmd->cmnd[0])) {
+	    case CMD_WRITE:
+		hostdata->stats.writes += 1;
+		break;
+	    case CMD_READ:
+	        hostdata->stats.reads += 1;
+	        break;
+	    case CMD_MISC:
+	        hostdata->stats.miscs += 1;
+	        break;
+	    }
+	    return;
+	}
+    }
+
+not_busy:
+    sti ();
+}
+
+static inline void acornscsi_startsendcommand (struct Scsi_Host *instance)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+
+    if (set_bit (0, (void *)&hostdata->busy) == 0)
+	acornscsi_sendcommand (instance, hostdata);
+    hostdata->busy = 0;
+}
+
+static void acornscsi_done (struct Scsi_Host *instance, struct as_cmnd *ascmd)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    Scsi_Cmnd *SCpnt;
+
+    sbic_write (instance, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+
+    SCpnt = ascmd->cmd;
+
+    if (!SCpnt)
+	panic ("scsi%d :  fatal : null command in acornscsi_done\n", instance->host_no);
+#if 0
+    printk ("scsi%d : target %d connected in %d, finished %d, transferred %d\n",
+    	instance->host_no, SCpnt->target, ascmd->connect_jiffies - ascmd->queued_jiffies,
+    		 finished_jiffies - ascmd->queued_jiffies, SCpnt->request_bufflen);
+#endif
+    ascmd->cmd     = NULL;
+    ascmd->next    = NULL;
+    ascmd->buf0len = 0;
+    ascmd->buf1len = 0;
+    ascmd->used    = 0;
+
+    hostdata->dev[SCpnt->target].busy &= ~(1 << SCpnt->lun);
+
+    if (!SCpnt->scsi_done)
+	panic ("scsi%d :  fatal : null scsi_done function in acornscsi_done\n", instance->host_no);
+
+    SCpnt->scsi_done (SCpnt);
+}
+
+static inline void acornscsi_savepointers (struct Scsi_Host *instance,
+				struct acornscsi_hostdata *hostdata)
+{
+    struct as_cmnd *ascmd = hostdata->connected;
+
+    dmac_write (instance, MASKREG, MASK_ON);
+    
+    ascmd->saved_sbic_transcnt = hostdata->tmpsbiclen;
+    ascmd->saved_dmac_address  = (dmac_read (instance, TXADRHI) << 16) |
+    				 (dmac_read (instance, TXADRMD) << 8) |
+    				  dmac_read (instance, TXADRLO);
+    ascmd->saved_dmac_transcnt = (dmac_read (instance, TXCNTHI) << 8) |
+    				  dmac_read (instance, TXCNTLO);
+    ascmd->saved = 1;
+#if (DEBUG & DEBUG_DISCON)
+    printk ("scsi%d :  debug : discon: sl %06lX da %06lX dl %04lX\n", instance->host_no,
+		ascmd->saved_sbic_transcnt, ascmd->saved_dmac_address,
+		ascmd->saved_dmac_transcnt);
+#endif
+}
+
+/*
+ * Function : acornscsi_disconnect (struct Scsi_Host *instance)
+ *
+ * Purpose  : disconnect the current command & put onto disconnected queue
+ *
+ * Params   : instance - host data
+ *
+ * Notes:
+ * We appear to have a race condition between this exiting, sending the next command,
+ * and a device on the bus reselecting us...
+ */
+static inline void acornscsi_disconnect (struct Scsi_Host *instance)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    struct as_dev *dev;
+    struct as_cmnd *ascmd;
+    unsigned long flags;
+
+    dev = &hostdata->dev[hostdata->connected->cmd->target];
+
+    dmac_write (instance, MASKREG, MASK_ON);
+
+    save_flags (flags);
+    cli ();
+    ascmd = hostdata->connected;
+    hostdata->connected = NULL;
+
+    ascmd->next = dev->disconnected;
+    dev->disconnected = ascmd;
+    restore_flags (flags);
+
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+    printk("scsi%d : command for target %d lun %d tag %d moved to disconnected queue\n",
+	instance->host_no, ascmd->cmd->target, ascmd->cmd->lun, ascmd->cmd->tag);
+#endif
+}
+
+/*
+ * Function : acornscsi_reconnect (struct Scsi_Host *instance)
+ *
+ * Purpose  : restore a previously saved command state
+ *
+ * Params   : instance - host data
+ */
+static inline int acornscsi_reconnect (struct Scsi_Host *instance)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    struct as_dev *dev;
+    struct as_cmnd *ascmd, **asprev;
+    int sourceid, sourcelun, sourcetag = 0;
+
+    sourceid = sbic_read (instance, SOURCEID);
+
+    if (hostdata->connected) {
+	printk ("scsi%d : warning: reconnecting while connected to target %d\n",
+	    instance->host_no, hostdata->connected->cmd->target);
+	hostdata->connected = NULL;
+    }
+
+    if (!(sourceid & 8)) {
+	printk ("scsi%d : warning: invalid source id after reselection - chip/device fault?\n",
+	    instance->host_no);
+	return 0;
+    }
+
+    sourceid &= 7;
+    sourcelun = sbic_read (instance, DATA); /* should this be target lun or data? */
+    sourcelun &= 7;
+
+    dev = &hostdata->dev[sourceid];
+
+    for (asprev = &dev->disconnected; *asprev; asprev = &(*asprev)->next) {
+    	ascmd = *asprev;
+	if (ascmd->cmd->target != sourceid) {
+	    printk ("scsi%d : warning: command on wrong disconnected queue %d instead of %d\n",
+		instance->host_no, ascmd->cmd->target, sourceid);
+	}
+	if (ascmd->cmd->lun == sourcelun && ascmd->cmd->tag == sourcetag) {
+	    *asprev = ascmd->next;
+	    ascmd->next = NULL;
+	    hostdata->connected = ascmd;
+#if (DEBUG & DEBUG_QUEUES)
+	    printk ("scsi%d :  debug : command for target %d lun %d tag %d reconnected\n",
+		instance->host_no, ascmd->cmd->target, ascmd->cmd->lun, ascmd->cmd->tag);
+#endif
+	    return 1;
+	}
+    }
+    printk("scsi%d : warning: target %d lun %d tag %d not in disconnect_queue.\n",
+	instance->host_no, sourceid, sourcelun, sourcetag);
+    return 0;
+}
+
+/*
+ * Function : acornscsi_forcedisconnect (struct Scsi_Host *instance)
+ *
+ * Purpose  : force a disconnect from the bus
+ *
+ * Params   : instance - hostdata
+ */
+static inline void acornscsi_forcedisconnect (struct Scsi_Host *instance)
+{
+}
+
+/*
+ * Function : acornscsi_startabort (struct Scsi_Host *instance)
+ *
+ * Purpose  : start aborting a connected command
+ *
+ * Params   : instance - hostdata
+ */
+static inline void acornscsi_startabort (struct Scsi_Host *instance)
+{
+	acornscsi_printstatus (instance);
+}
+
+/* ==========================================================================================
+ * Interrupt routines.
+ */
+static int acornscsi_sbicintr (struct Scsi_Host *instance)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    struct as_cmnd *ascmd;
+    int asr, ssr;
+
+    asr = sbic_read (instance, ASR);
+    if (!(asr & ASR_INT))
+	return INTR_CONTINUE;
+
+    ssr = sbic_read (instance, SSR);
+#if (DEBUG & (DEBUG_DMA|DEBUG_RECON|DEBUG_PHASES|DEBUG_WRITE))
+    print_sbic_status(asr, ssr, sbic_read (instance, CMNDPHASE));
+#endif
+    ADD_STATUS(ssr);
+    ADD_STATUS(hostdata->phase);
+
+    ascmd = hostdata->connected;
+
+    /*
+     * This is just one big switch statement
+     */
+
+    switch (ssr) {
+
+/* ---------------------- Reset -------------------------------------------------------- */
+    
+    case 0x00: /* reset state - not advanced */
+	printk ("scsi%d : reset in standard mode but wanted advanced mode.\n",
+	    instance->host_no);
+    case 0x01: /* reset state - advanced */
+	sbic_write (instance, CTRL, INIT_SBICDMA | CTRL_IDI);
+	sbic_write (instance, TIMEOUT, 10);
+	sbic_write (instance, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+	return INTR_CONTINUE;
+
+/* ---------------------- Select ------------------------------------------------------- */
+	
+    case 0x11: /* select command ok */
+        switch (hostdata->phase) {
+	case PHASE_CONNECTING:
+	    if (!hostdata->connecting) {
+		printk ("scsi%d : warning: select ok but no connecting command\n",
+		    instance->host_no);
+		goto abort_connect;
+	    }
+	    hostdata->connected = hostdata->connecting;
+	    hostdata->connecting = NULL;
+	    hostdata->phase = PHASE_CONNECTED;
+	    hostdata->connected->buffer = 2;
+	    hostdata->msgout = NOP;
+	    hostdata->last_message = NOP;
+	    hostdata->connected->connect_jiffies = jiffies;
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
+	    printk ("scsi%d :  debug : command connected to target %d\n",
+	    	instance->host_no, hostdata->connected->cmd->target);
+#endif
+	    return INTR_CONNECTED;
+
+	case PHASE_CONNECTING_FOR_ABORT:
+	    if (!hostdata->aborting) {
+		printk ("scsi%d : warning: select ok but no connecting aborting command\n",
+		    instance->host_no);
+		goto abort_connect;
+	    }
+	    hostdata->connected = hostdata->aborting;
+	    hostdata->aborting = NULL;
+	    hostdata->phase = PHASE_CONNECTED_FOR_ABORT;
+	    hostdata->msgout = ABORT;
+	    hostdata->last_message = NOP;
+	    hostdata->connected->buffer = 2;
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_CONNECT))
+	    printk ("scsi%d :  debug : abort command connected to target %d\n",
+	    	instance->host_no, hostdata->connected->cmd->target);
+#endif
+	    return INTR_CONNECTED;
+
+	default:
+	    printk ("scsi%d : warning: select connected to target with invalid phase (%X)\n",
+		instance->host_no, hostdata->phase);
+	abort_connect:
+	    /* At this point we have lost the command totally (and hence can't return an
+	     * error until the upper code causes an abort.
+	     */
+	    acornscsi_printstatus (instance);
+	    acornscsi_forcedisconnect (instance);
+	    return INTR_NOTCONNECTED;
+	}
+
+    case 0x42:		/* select timeout */
+	switch (hostdata->phase) {
+	case PHASE_CONNECTING:
+	    ascmd = hostdata->connecting;
+	    hostdata->connecting = NULL;
+	    ascmd->cmd->result = DID_BAD_TARGET << 16;
+	    hostdata->phase = PHASE_IDLE;
+	    acornscsi_done (instance, ascmd);
+	    return INTR_NOTCONNECTED;
+
+	case PHASE_CONNECTING_FOR_ABORT:
+	    ascmd = hostdata->aborting;
+	    hostdata->aborting = NULL;
+	    ascmd->cmd->result = DID_BAD_TARGET << 16;
+	    hostdata->phase = PHASE_IDLE;
+	    acornscsi_done (instance, ascmd);
+	    return INTR_NOTCONNECTED;
+
+	default:
+	    printk ("scsi%d : warning: select timeout for target %d in wrong phase\n",
+	    	instance->host_no, hostdata->connecting->cmd->target);
+	    acornscsi_printstatus (instance);
+	    return INTR_NOTCONNECTED;
+	}
+
+    case 0x81: /* reselection occurred */
+	/* for this, we need to add extra code to allow us to keep track of whether this
+	 * is SCSI 2 tagged or not.  I am not sure how this part is supposed to work.
+	 *
+	 * For the moment, I will assume that it works the same way that we tell the
+	 * device, ie. SELECT->MESSAGE(IDENTIFY)->MESSAGE(SIMPLE_TAG)->MESSAGE(TAGID)
+	 */
+	if (acornscsi_reconnect (instance)) {
+	    sbic_write (instance, CMND, CMND_NEGATEACK);
+	    hostdata->phase = PHASE_RECONNECTED;
+	}
+	return INTR_CONNECTED;
+	
+    case 0x41:		/* unexpected disconnect aborted command */
+	if (!ascmd) {
+	    printk ("scsi%d : warning: unexpected disconnect without connected command\n",
+		instance->host_no);
+	    return INTR_NOTCONNECTED;
+	}
+	hostdata->connected = NULL;
+	ascmd->cmd->result = DID_ABORT << 16;
+	acornscsi_done (instance, ascmd);
+	hostdata->phase = PHASE_IDLE;
+	return INTR_NOTCONNECTED;
+
+    case 0x85: /* disconnection occurred */
+	if (ascmd == NULL) {
+	    printk ("scsi%d : warning: disconnect with no connected command\n",
+	    	instance->host_no);
+	    return INTR_NOTCONNECTED;
+	}
+	switch (hostdata->phase) {
+	case PHASE_FINISHED:
+	    hostdata->connected = NULL;
+	    hostdata->stats.fins += 1;
+	    ascmd->cmd->result = ascmd->cmd->SCp.Status | (ascmd->cmd->SCp.Message << 8);
+	    hostdata->phase = PHASE_IDLE;
+	    acornscsi_done (instance, ascmd);
+	    return INTR_NOTCONNECTED;
+
+	case PHASE_DISCONNECTING:
+	    acornscsi_disconnect (instance);
+	    hostdata->phase = PHASE_IDLE;
+	    return INTR_NOTCONNECTED;
+
+	default:
+	    printk ("scsi%d : warning: invalid phase in disconnect (%X)\n",
+		instance->host_no, hostdata->phase);
+	    acornscsi_printstatus (instance);
+	}
+    	return INTR_NOTCONNECTED;
+
+/* ---------------------- data out ----------------------------------------------------- */
+
+    case 0x88: /* connect - data out [only after reconnection] */
+	if (hostdata->phase != PHASE_RECONNECTED) {
+	    printk ("scsi%d : warning: invalid phase for reconnected data out (%X)\n",
+		instance->host_no, hostdata->phase);
+	    return INTR_CONNECTED;
+	}
+
+#if (DEBUG & (DEBUG_DISCON|DEBUG_WRITE))
+	    printk ("scsi%d :  debug : recon: buffer %d ",
+		instance->host_no, ascmd->buffer);
+#endif
+	if (ascmd->buffer == 0 || ascmd->buffer == 1) {
+	    if (!ascmd->saved) {
+		printk ("scsi%d : warning: no saved data for reconnected command\n",
+		    instance->host_no);
+		return INTR_CONNECTED;
+	    }
+	    ascmd->saved = 0;
+
+	    dmac_write (instance, MASKREG, MASK_ON);
+	    dmac_write (instance, TXCNTLO, ascmd->saved_dmac_transcnt);
+	    dmac_write (instance, TXCNTHI, ascmd->saved_dmac_transcnt >> 8);
+	    dmac_write (instance, TXADRLO, ascmd->saved_dmac_address);
+	    dmac_write (instance, TXADRMD, ascmd->saved_dmac_address >> 8);
+	    dmac_write (instance, TXADRHI, ascmd->saved_dmac_address >> 16);
+	    dmac_write (instance, MODECON, DMAC_WRITE);
+	    dmac_write (instance, MASKREG, MASK_OFF);
+	    sbic_write (instance, TRANSCNTL, ascmd->saved_sbic_transcnt);
+	    sbic_write (instance, TRANSCNTM, ascmd->saved_sbic_transcnt >> 8);
+	    sbic_write (instance, TRANSCNTH, ascmd->saved_sbic_transcnt >> 16);
+#if (DEBUG & (DEBUG_DISCON|DEBUG_WRITE))
+	    printk ("sl %06lX da %06lX dl %04lX\n", ascmd->saved_sbic_transcnt,
+		ascmd->saved_dmac_address, ascmd->saved_dmac_transcnt);
+#endif
+	    sbic_write (instance, CMND, CMND_XFERINFO);
+	    hostdata->phase = PHASE_DATAOUT;
+	    return INTR_CONNECTED;
+	}
+	ascmd->saved = 0;
+	goto data_out_common;
+
+    case 0x18: /* transfer completed - data out */
+	if (hostdata->phase != PHASE_COMMANDSENT) {
+	    printk ("scsi%d : warning: invalid phase for data out (%X)\n",
+		instance->host_no, hostdata->phase);
+	    acornscsi_startabort (instance);
+	    return INTR_CONNECTED;
+	}
+    data_out_common:
+	ascmd->buffer = 0;
+	acornscsi_writefrombh (instance, ascmd, 0);
+	acornscsi_setupdmac (instance, DMAC_WRITE, ascmd->buf0addr, ascmd->buf0len);
+	sbic_write (instance, TRANSCNTL, ascmd->totallen);
+	sbic_write (instance, TRANSCNTM, ascmd->totallen >> 8);
+	sbic_write (instance, TRANSCNTH, ascmd->totallen >> 16);
+	sbic_write (instance, CMND, CMND_XFERINFO);
+	hostdata->phase = PHASE_DATAOUT;
+	acornscsi_writefrombh (instance, ascmd, 1);
+#if (DEBUG & (DEBUG_DISCON|DEBUG_WRITE))
+	printk ("sl %06lX da %06lX dl %04lX\n", ascmd->totallen, ascmd->buf0addr,
+		ascmd->buf0len);
+#endif
+	return INTR_CONNECTED;
+
+/* ---------------------- data in ------------------------------------------------------ */
+
+    case 0x89: /* connect - data in [only after reconnection] */
+	if (hostdata->phase != PHASE_RECONNECTED) {
+	    printk ("scsi%d : warning: invalid phase for reconnected data in (%X)\n",
+		instance->host_no, hostdata->phase);
+	    return INTR_CONNECTED;
+	}
+
+#if (DEBUG & DEBUG_DISCON)
+	printk ("scsi%d :  debug : recon: buffer %d ",
+		instance->host_no, ascmd->buffer);
+#endif
+	if (ascmd->buffer == 0 || ascmd->buffer == 1) {
+	    if (!ascmd->saved) {
+		printk ("scsi%d : warning: no saved data for reconnected command\n",
+		    instance->host_no);
+		return INTR_CONNECTED;
+	    }
+	    ascmd->saved = 0;
+
+	    dmac_write (instance, MASKREG, MASK_ON);
+	    dmac_write (instance, TXCNTLO, ascmd->saved_dmac_transcnt);
+	    dmac_write (instance, TXCNTHI, ascmd->saved_dmac_transcnt >> 8);
+	    dmac_write (instance, TXADRLO, ascmd->saved_dmac_address);
+	    dmac_write (instance, TXADRMD, ascmd->saved_dmac_address >> 8);
+	    dmac_write (instance, TXADRHI, ascmd->saved_dmac_address >> 16);
+	    dmac_write (instance, MODECON, DMAC_READ);
+	    dmac_write (instance, MASKREG, MASK_OFF);
+	    sbic_write (instance, TRANSCNTL, ascmd->saved_sbic_transcnt);
+	    sbic_write (instance, TRANSCNTM, ascmd->saved_sbic_transcnt >> 8);
+	    sbic_write (instance, TRANSCNTH, ascmd->saved_sbic_transcnt >> 16);
+#if (DEBUG & DEBUG_DISCON)
+	    printk ("sl %06lX da %06lX dl %04lX\n", ascmd->saved_sbic_transcnt,
+		ascmd->saved_dmac_address, ascmd->saved_dmac_transcnt);
+#endif
+	    sbic_write (instance, CMND, CMND_XFERINFO);
+	    hostdata->phase = PHASE_DATAIN;
+	    return INTR_CONNECTED;
+	}
+	ascmd->saved = 0;
+	goto data_in_common;
+
+    case 0x19: /* transfer completed - data in */
+	if (hostdata->phase != PHASE_COMMANDSENT) {
+	    printk ("scsi%d : warning: invalid phase for data in (%X)\n",
+		instance->host_no, hostdata->phase);
+	    acornscsi_startabort (instance);
+	    return INTR_CONNECTED;
+	}
+    data_in_common:
+	ascmd->buffer = 0;
+
+	if (ascmd->totallen > 4096)
+	    ascmd->buf0len = 4096;
+	else
+	    ascmd->buf0len = ascmd->totallen;
+
+	acornscsi_setupdmac (instance, DMAC_READ, ascmd->buf0addr, ascmd->buf0len);
+
+	sbic_write (instance, TRANSCNTL, ascmd->totallen);
+	sbic_write (instance, TRANSCNTM, ascmd->totallen >> 8);
+	sbic_write (instance, TRANSCNTH, ascmd->totallen >> 16);
+#if (DEBUG & DEBUG_DISCON)
+	printk ("sl %06lX da %06lX dl %04X\n", ascmd->totallen, ascmd->buf0addr,
+		ascmd->buf0len);
+#endif
+	sbic_write (instance, CMND, CMND_XFERINFO);
+	hostdata->phase = PHASE_DATAIN;
+	return INTR_CONNECTED;
+
+/* ---------------------- command out -------------------------------------------------- */
+
+    case 0x1a: /* transfer completed - command info next */
+    case 0x8a: {/* connection - command phase out */
+	Scsi_Cmnd *SCpnt;
+	int i;
+	
+	if (hostdata->phase != PHASE_COMMAND) {
+	    printk ("scsi%d : warning: invalid phase for command (%X)\n",
+		instance->host_no, hostdata->phase);
+	    acornscsi_startabort (instance);
+	    return INTR_CONNECTED;
+	}
+	SCpnt = hostdata->connected->cmd;
+
+	sbic_write (instance, TRANSCNTL, SCpnt->cmd_len);
+	sbic_write (instance, TRANSCNTM, 0);
+	sbic_write (instance, TRANSCNTH, 0);
+	sbic_write (instance, CMND, CMND_XFERINFO);
+
+	for (i = 0; i < SCpnt->cmd_len; i++) {
+	    int asr;
+	    do {
+		asr = sbic_read (instance, ASR) & (ASR_INT | ASR_BSY | ASR_DBR);
+	    } while (asr == ASR_BSY);
+	    if (!(asr & ASR_DBR)) {
+		printk ("scsi%d : warning: interrupt while sending command\n",
+			instance->host_no);
+		return INTR_CONNECTED;
+	    }
+	    sbic_write (instance, DATA, SCpnt->cmnd[i]);
+	}
+	hostdata->phase = PHASE_COMMANDSENT;
+        
+	return INTR_CONNECTED;
+    }
+
+/* ---------------------- status in ---------------------------------------------------- */
+
+    case 0x1b: /* transfer completed - status phase in */
+    case 0x8b: /* connection - status phase in [only after reconnection] */
+	switch (hostdata->phase) {
+	case PHASE_RECONNECTED:
+	    if (ssr == 0x8b)
+		break;
+	    goto complain_status_in;
+
+	case PHASE_COMMANDSENT:
+	case PHASE_DATAIN:
+	case PHASE_DATAOUT:
+	    if (ssr == 0x1b)
+		break;
+
+	complain_status_in:
+	default:
+	    printk ("scsi%d : warning: invalid phase (%02X) in status in (%02X)\n",
+		instance->host_no, hostdata->phase, ssr);
+	    acornscsi_printstatus (instance);
+	    acornscsi_startabort (instance);
+	    return INTR_CONNECTED;
+	}
+	sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
+	while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
+	hostdata->connected->cmd->SCp.Status = sbic_read (instance, DATA);
+	hostdata->phase = PHASE_STATUSIN;
+	return INTR_CONNECTED;
+
+    case 0x4b: { /* unexpected status info */
+	short residual;
+
+	dmac_write (instance, MASKREG, MASK_ON);
+	residual = ((dmac_read (instance, TXCNTHI) << 8) | dmac_read (instance, TXCNTLO)) + 1;
+
+	sbic_write (instance, TRANSCNTH, 0);
+	sbic_write (instance, TRANSCNTM, 0);
+	sbic_write (instance, TRANSCNTL, 0);
+
+	switch (hostdata->phase) {
+	case PHASE_DATAIN:
+	    acornscsi_readtobh (instance, hostdata, ascmd, residual);
+	    break;
+
+	case PHASE_DATAOUT:
+	case PHASE_COMMAND:
+	case PHASE_COMMANDSENT:
+	    break;
+
+	default:
+	    printk ("scsi%d : warning: invalid phase in unexpected status in (%X)\n",
+		instance->host_no, hostdata->phase);
+	    acornscsi_printstatus (instance);
+	    return INTR_CONNECTED;
+	}
+
+	sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
+	while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
+	hostdata->connected->cmd->SCp.Status = sbic_read (instance, DATA);
+	hostdata->phase = PHASE_STATUSIN;
+
+	return INTR_CONNECTED;
+	}
+
+/* ---------------------- message out -------------------------------------------------- */
+
+    case 0x8e: /* connection - message out phase [only after reconnection] */
+	switch (hostdata->phase) {
+	case PHASE_CONNECTED:
+#ifdef SCSI2_TAG
+	    if (ascmd->cmd->device->tagged_queue) {
+		int i;
+		sbic_write (instance, TRANSCNTL, 3);
+		sbic_write (instance, TRANSCNTM, 0);
+		sbic_write (instance, TRANSCNTH, 0);
+		sbic_write (instance, CMND, CMND_XFERINFO);
+		if (ascmd->cmd->device->tagged_queue)
+		    hostdata->last_message = SIMPLE_QUEUE_TAG;
+		for (i = 0; i < 3; i++) {
+		    do {
+			asr = sbic_read (instance, ASR) & (ASR_INT | ASR_BSY | ASR_DBR);
+		    } while (asr == ASR_BSY);
+		    if (!(asr & ASR_DBR)) {
+			printk ("scsi%d : warning: interrupt while sending tag\n",
+			    instance->host_no);
+			return INTR_CONNECTED;
+		    }
+		    switch (i) {
+		    case 0:
+			sbic_write (instance, DATA, IDENTIFY(1, hostdata->connected->cmd->lun));
+			break;
+		    case 1:
+			sbic_write (instance, DATA, SIMPLE_QUEUE_TAG);
+			break;
+		    case 2:
+			sbic_write (instance, DATA, hostdata->connected->cmd->tag);
+			break;
+		    }
+		}
+	    } else
+#endif
+	    {
+		sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
+		while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
+		sbic_write (instance, DATA, IDENTIFY(1, hostdata->connected->cmd->lun));
+	    }
+	    hostdata->phase = PHASE_COMMAND;
+	    return INTR_CONNECTED;
+
+	case PHASE_CONNECTED_FOR_ABORT:
+	    sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
+	    while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
+	    sbic_write (instance, DATA, ABORT);
+	    hostdata->last_message = ABORT;
+	    return INTR_CONNECTED;
+
+	default:
+	    printk ("scsi%d : warning: invalid phase in message out (%X)\n",
+		instance->host_no, hostdata->phase);
+	    acornscsi_printstatus (instance);
+	    return INTR_CONNECTED;
+	}
+
+/* ---------------------- message in --------------------------------------------------- */
+
+    case 0x1f: /* transfer completed - message in phase */
+    case 0x4f: /* transfer terminated - message in phase */
+    case 0x8f: /* connection - message in phase [only after reconnection] [???] */
+	hostdata->tmpsbiclen = sbic_read (instance, TRANSCNTL) |
+			      (sbic_read (instance, TRANSCNTM) << 8) |
+			      (sbic_read (instance, TRANSCNTH) << 16);
+	switch (hostdata->phase) {
+	case PHASE_STATUSIN:
+	case PHASE_DATAIN:
+	case PHASE_DATAOUT:
+	    break;
+
+	case PHASE_COMMANDSENT:
+	    if (ssr == 0x1f || ssr == 0x4f)
+		break;
+	    goto complain_msg_in;
+
+	case PHASE_RECONNECTED:
+	    if (ssr == 0x8f)
+	    	break;
+
+	default:
+	complain_msg_in:
+	    /* just complain - accept message in anyway otherwise host will hang until reset */
+	    printk ("scsi%d : warning: invalid phase (%02X) in message in (%02X)\n",
+		instance->host_no, hostdata->phase, ssr);
+	    acornscsi_printstatus (instance);
+	}
+	sbic_write (instance, CMND, CMND_XFERINFO|CMND_SBT);
+	while ((sbic_read (instance, ASR) & ASR_DBR) == 0);
+	hostdata->connected->cmd->SCp.Message = sbic_read (instance, DATA);
+	return INTR_CONNECTED;
+
+    case 0x20: {/* message in transfer paused */
+	int message = hostdata->connected->cmd->SCp.Message;
+#if 0
+printk ("scsi%d :  debug : message received:", instance->host_no);
+print_msg ((unsigned char *)&hostdata->connected->cmd->SCp.Message);
+printk ("\n");
+#endif
+	switch (message) {
+	case COMMAND_COMPLETE:
+	    sbic_write (instance, CMND, CMND_NEGATEACK);
+	    hostdata->phase = PHASE_FINISHED;
+	    return INTR_CONNECTED;
+
+	case SAVE_POINTERS:
+	    sbic_write (instance, CMND, CMND_NEGATEACK);
+	    acornscsi_savepointers (instance, hostdata);
+	    break;
+
+	case RESTORE_POINTERS:
+	    sbic_write (instance, CMND, CMND_NEGATEACK);
+	    break;
+
+	case DISCONNECT:
+	    sbic_write (instance, CMND, CMND_NEGATEACK);
+	    /* If the device has not issued a 'save pointers' message, do it. */
+	    if (!hostdata->connected->saved)
+		acornscsi_savepointers (instance, hostdata);
+	    hostdata->phase = PHASE_DISCONNECTING;
+	    return INTR_CONNECTED;
+
+	case LINKED_CMD_COMPLETE:
+	case LINKED_FLG_CMD_COMPLETE:
+#ifdef SCSI2_LINK
+	    /*
+	     * We don't support linked commands yet
+	     */
+	    if (0) {
+#if (DEBUG & DEBUG_LINKED)
+		printk ("scsi%d : target %d lun %d tag %d linked command complete\n",
+			instance->host_no, ascmd->connected->target, ascmd->connected->tag);
+#endif
+		/*
+		 * A linked command should only terminate with one of these messages
+		 * if there are more linked commands available.
+		 */
+		if (!ascmd->cmd->next_link) {
+		    printk ("scsi%d : warning: target %d lun %d tag %d linked command complete, but no next_link\n",
+			instance->host_no, ascmd->connected->target, ascmd->connected->tag);
+		    hostdata->msgout = ABORT;
+		    goto go_msg_out;
+		}
+		sbic_write (instance, CMND, CMND_NEGATEACK);
+
+		ascmd->cmd->next_link->tag = ascmd->cmd->tag;
+		/* scsi_done */
+		hostdata->connected->cmd = ascmd->cmd->next_link;
+		break;
+	    }
+#endif
+	    goto reject;
+
+	case MESSAGE_REJECT:
+	    sbic_write (instance, CMND, CMND_NEGATEACK);
+	    switch (hostdata->last_message) {
+#ifdef SCSI2_TAG
+	    case HEAD_OF_QUEUE_TAG:
+	    case ORDERED_QUEUE_TAG:
+	    case SIMPLE_QUEUE_TAG:
+		ascmd->cmd->device->tagged_queue = 0;
+		set_bit (ascmd->cmd->lun, &hostdata->dev[ascmd->cmd->target].busy);
+		printk ("scsi%d : disabling tagged queueing for target %d\n",
+		    instance->host_no, ascmd->cmd->target);
+		break;
+#endif
+	    default:
+	        break;
+	    }
+
+	default: /* reject message */
+	reject:
+	    hostdata->msgout = MESSAGE_REJECT;
+	    sbic_write (instance, CMND, CMND_ASSERTATN);
+	    while (sbic_read (instance, ASR) & ASR_BSY);
+	    if (!(sbic_read (instance, ASR) & ASR_INT)) {
+		sbic_write (instance, CMND, CMND_NEGATEACK);
+	    }
+	    break;
+	}
+	}
+	return INTR_CONNECTED;
+
+    default:
+	printk ("scsi%d : unknown interrupt number %02X\n", instance->host_no, ssr);
+	acornscsi_printstatus (instance);
+	return INTR_CONTINUE;
+    }
+}
+
+static void acornscsi_intr(int irq, struct pt_regs *regs)
+{
+    struct Scsi_Host *instance;
+    int need_command, need_command_done;
+
+    do {
+    	need_command_done = 0;
+	for (instance = first_instance; instance && (instance->hostt == the_template);
+		instance = instance->next) {
+	    if (instance->irq != irq)
+	        continue;
+
+	    need_command = 0;
+
+	    if (read_irqstat(instance) & 2)
+		/* setup for next buffer & transfer current buffer */
+		if (acornscsi_handletransfer(instance))
+		    continue;
+
+	    if (read_irqstat(instance) & 8)
+	        switch (acornscsi_sbicintr (instance)) {
+		case INTR_NOTCONNECTED:
+		    need_command = 1;
+		    break;
+		case INTR_CONNECTED:
+		    need_command_done = 1;
+		    break;
+		}
+	    if (need_command) {
+	        acornscsi_startsendcommand (instance);
+	        need_command_done = 1;
+	    }
+	}
+    }
+    while(need_command_done);
+}
+
+/*=============================================================================================
+ * Interfaces between interrupt handler and rest of scsi code
+ */
+
+/*
+ * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose  : queues a SCSI command
+ *
+ * Params   : cmd  - SCSI command
+ *            done - function called on completion, with pointer to command descriptor
+ *
+ * Returns  : 0
+ *
+ */
+int acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)cmd->host->hostdata;
+    struct as_cmnd *as_cmd;
+    int i;
+#if (DEBUG & DEBUG_QUEUES)
+    const char *where;
+#endif
+
+    if (!done) {
+    	/* there should be some way of rejecting errors like this without panicing... */
+	panic ("scsi%d :  fatal : queuecommand called with NULL done function [cmd=%p]\n",
+		cmd->host->host_no, cmd);
+	return -EINVAL;
+    }
+
+#if (DEBUG & DEBUG_NO_WRITE)
+    if (acornscsi_cmdtype (cmd->cmnd[0]) == CMD_WRITE) {
+	printk ("scsi%d : id %d: WRITE attempted with NO_WRITE flag set\n",
+	    cmd->host->host_no, cmd->target);
+	cmd->result = DID_ERROR << 16;
+	done (cmd);
+	return 0;
+    }
+#endif
+
+    cmd->host_scribble = NULL;
+    cmd->scsi_done = done;
+    cmd->result = 0;
+    cmd->SCpdirection = acornscsi_writedirection (cmd->cmnd[0]);
+
+    acornscsi_initialisebuffer (cmd);
+
+    cmd->tag = 0;
+
+    for (i = 0, as_cmd = hostdata->queuedcommands; i < CAN_QUEUE; i++, as_cmd ++) {
+	if (set_bit (0, (void *)&as_cmd->used) == 0) {
+	    if (as_cmd->cmd) {
+		printk ("scsi%d : warning: free command found wasn't free\n",
+			cmd->host->host_no);
+		continue;
+	    }
+	    as_cmd->cmd = cmd;
+
+	    if (cmd->cmnd[0] == REQUEST_SENSE) {
+		cli ();
+		as_cmd->next = hostdata->dev[cmd->target].issue_head;
+		hostdata->dev[cmd->target].issue_head = as_cmd;
+		sti ();
+#if (DEBUG & DEBUG_QUEUES)
+		where = "head";
+#endif
+	    } else {
+		cli ();
+		as_cmd->next = NULL;
+		if (hostdata->dev[cmd->target].issue_head == NULL)
+		    hostdata->dev[cmd->target].issue_head = as_cmd;
+		if (hostdata->dev[cmd->target].issue_tail)
+		    hostdata->dev[cmd->target].issue_tail->next = as_cmd;
+		hostdata->dev[cmd->target].issue_tail = as_cmd;
+		sti ();
+#if (DEBUG & DEBUG_QUEUES)
+		where = "tail";
+#endif
+	    }
+	    hostdata->stats.queues += 1;
+#if (DEBUG & DEBUG_QUEUES)
+	    printk ("scsi%d :  debug : command put on %s of queue for target %d\n",
+			cmd->host->host_no, where, cmd->target);
+#endif
+	    as_cmd->buffer = 2;
+	    as_cmd->totallen = cmd->request_bufflen;
+	    as_cmd->queued_jiffies = jiffies;
+	    acornscsi_startsendcommand (cmd->host);
+	    return 0;
+	}
+    }
+
+    printk ("scsi%d :  error : could not find a free slot for command?\n", cmd->host->host_no);
+
+    cmd->result = DID_ERROR << 16;
+    done (cmd);
+    return -1;
+}
+
+int acornscsi_abort(Scsi_Cmnd * cmd)
+{
+    struct Scsi_Host *instance = cmd->host;
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *) instance->hostdata;
+    struct as_cmnd *ascmd, *asprev;
+
+    hostdata->stats.aborts += 1;
+
+    printk("acornscsi_abort: ");
+    print_sbic_status(sbic_read (instance, ASR), sbic_read (instance, SSR),
+			sbic_read (instance, CMNDPHASE));
+
+    /*
+     * Check to see if the command hasn't been issued.  If so, just remove it from the queue.
+     */
+    for (ascmd = hostdata->dev[cmd->target].issue_head, asprev = NULL; ascmd;
+		asprev = ascmd, ascmd = ascmd->next) {
+
+	if (ascmd->cmd == cmd) {
+	    if (ascmd == hostdata->dev[cmd->target].issue_tail)
+		hostdata->dev[cmd->target].issue_tail = asprev;
+	    if (asprev)
+	        asprev->next = ascmd->next;
+	    else
+	        hostdata->dev[cmd->target].issue_head = ascmd->next;
+            ascmd->next = NULL;
+	    sti();
+
+	    ascmd->cmd->result = DID_ABORT << 16;
+#if (DEBUG & DEBUG_ABORT)
+	    printk("scsi%d : abort removed command from issue queue\n", instance->host_no);
+#endif
+	    acornscsi_done (instance, ascmd);
+	    return SCSI_ABORT_SUCCESS;
+	}
+    }
+
+    /*
+     * If the command is connected, try to abort it
+     */
+    if (hostdata->connected && hostdata->connected->cmd == cmd) {
+	sti();
+#if (DEBUG & DEBUG_ABORT)
+	printk("scsi%d : aborting connected command\n", instance->host_no);
+#endif
+	hostdata->connected->cmd->result = DID_ABORT << 16; /* Hmm... doesn't seem to work.. */
+	sbic_write (instance, CMND, CMND_ASSERTATN); /* DISCONNECT? */
+	return SCSI_ABORT_PENDING;
+    }
+
+#if 0 /* we can now schedule an abort of a command while one is executing */
+    /*
+     * If a command is connected, we can't do anything to abort it at the moment.
+     */
+    if (hostdata->connected) {
+	sti();
+#if (DEBUG & DEBUG_ABORT)
+	printk("scsi%d : abort failed, command connected\n", instance->host_no);
+#endif
+	return SCSI_ABORT_BUSY;
+    }
+#endif
+
+    /*
+     * If the command is disconnected, and there are no connected commands, reconnect
+     * and send abort message
+     */
+    for (ascmd = hostdata->dev[cmd->target].disconnected, asprev = NULL; asprev;
+		asprev = ascmd, ascmd = ascmd->next) {
+	if (ascmd->cmd == cmd) {
+#if (DEBUG & DEBUG_ABORT)
+	    printk("scsi%d : aborting disconnected command\n", instance->host_no);
+#endif
+	    if (asprev)
+		asprev->next = ascmd->next;
+	    else
+	        hostdata->dev[cmd->target].disconnected = ascmd->next;
+	    ascmd->next = NULL;
+
+	    hostdata->aborting = ascmd;
+	    sti ();
+
+	    acornscsi_startsendcommand (instance);
+	    return SCSI_ABORT_PENDING;
+	}
+    }
+
+    sti();
+    printk ("scsi%d : warning: SCSI command probably completed successfully before abortion\n",
+			instance->host_no);
+while (1);
+    return SCSI_ABORT_NOT_RUNNING;
+}
+
+int acornscsi_reset(Scsi_Cmnd * cmd)
+{
+    struct Scsi_Host *instance = cmd->host;
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    
+    hostdata->stats.resets += 1;
+
+    printk("acornscsi_reset:\n");
+    print_sbic_status(sbic_read (instance, ASR), sbic_read (instance, SSR), sbic_read (instance, CMNDPHASE));
+    hostdata->connected = NULL;
+    sbic_reset(instance);
+#if 0
+    cmd->result = DID_RESET << 16;
+    acornscsi_done (instance, cmd);
+#endif
+    return SCSI_RESET_SUCCESS;
+}
+
+/*==============================================================================================
+ * initialisation & miscellaneous support
+ */
+static struct expansion_card *ecs[4];
+
+static void acornscsi_init (struct Scsi_Host *instance)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *) instance->hostdata;
+    int i;
+
+    hostdata->busy = 0;
+    hostdata->phase = 0;
+    hostdata->page_reg = 0x40;
+    hostdata->connecting = NULL;
+    hostdata->aborting = NULL;
+    hostdata->connected = NULL;
+
+    for (i = 0; i < CAN_QUEUE; i++) {
+	hostdata->queuedcommands[i].used     = 0;
+	hostdata->queuedcommands[i].cmd      = NULL;
+	hostdata->queuedcommands[i].next     = NULL;
+	hostdata->queuedcommands[i].buf0addr = i * 0x2000;
+	hostdata->queuedcommands[i].buf1addr = i * 0x2000 + 0x1000;
+	hostdata->queuedcommands[i].buf0len  = 0;
+	hostdata->queuedcommands[i].buf1len  = 0;
+    }
+
+    for (i = 0; i < 8; i++) {
+    	hostdata->dev[i].issue_head   = NULL;
+    	hostdata->dev[i].issue_tail   = NULL;
+    	hostdata->dev[i].disconnected = NULL;
+    	hostdata->dev[i].busy = 0;
+    }
+
+    hostdata->stats.queues = 0;
+    hostdata->stats.removes= 0;
+    hostdata->stats.fins   = 0;
+    hostdata->stats.reads  = 0;
+    hostdata->stats.writes = 0;
+    hostdata->stats.aborts = 0;
+    hostdata->stats.resets = 0;
+    hostdata->stats.disconnects = 0;
+
+    page_write(instance, 0x40);
+
+    if (!the_template)
+    {
+	the_template = instance->hostt;
+	first_instance = instance;
+    }
+
+    /* setup sbic - WD33C93A */
+    sbic_reset(instance);
+
+    /* setup dmac - uPC71071 */
+    dmac_write (instance, INIT, 0);
+    dmac_write (instance, INIT, INIT_8BIT);
+    dmac_write (instance, CHANNEL, CHANNEL_0);
+    dmac_write (instance, DEVCON0, INIT_DEVCON0);
+    dmac_write (instance, DEVCON1, INIT_DEVCON1);
+}
+
+int acornscsi_detect(Scsi_Host_Template * tpnt)
+{
+    static int acornscsi_prods[] = { PRODS };
+    static int acornscsi_manus[] = { MANUS };
+    int i, count = 0;
+    struct Scsi_Host *instance;
+
+    tpnt->proc_dir = &proc_scsi_acornscsi;
+
+    for (i = 0; i < 4; i++)
+    	ecs[i] = NULL;
+
+    while(1) {
+	ecs[count] = ecard_find(0, sizeof(acornscsi_prods), acornscsi_prods, acornscsi_manus);
+	if(!ecs[count])
+	    break;
+
+	instance = scsi_register (tpnt, sizeof(struct acornscsi_hostdata));
+	instance->io_port = ((int)ecs[count]->r_podaddr & ~0x003C0000) >> 2;
+	instance->irq = ecs[count]->irq;
+
+	if (instance->irq != 0xff) {
+	    if (request_irq(instance->irq, acornscsi_intr, SA_INTERRUPT, "acorn scsi"))
+	    {
+		printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+		    instance->host_no, instance->irq);
+		instance->irq = 0xff;
+	    }
+	}
+	if (instance->irq == 0xff) {
+	    printk("scsi%d : interrupts not enabled. For better interactive performance,\n",
+		instance->host_no);
+	    printk("scsi%d : please find out why I cannot get the interrupt.\n",
+		instance->host_no);
+	}
+
+	ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */
+	acornscsi_init(instance);
+
+	++count;
+    }
+    return count;
+}
+
+const char *acornscsi_info(struct Scsi_Host *host)
+{
+    static char string[100], *p;
+
+    p = string;
+    
+    p += sprintf (string, "%s at port %X irq", host->hostt->name, host->io_port);
+    if (host->irq == 0xff)
+	p += sprintf (p, "s disabled");
+    else
+	p += sprintf (p, " %d", host->irq);
+    sprintf (p, " version %d.%02d", VER_MAJOR, VER_MINOR);
+
+    return string;
+}
+
+int acornscsi_proc_info(char *buffer, char **start, off_t offset,
+			int length, int host_no, int inout)
+{
+    int len, pos, begin, first;
+    struct Scsi_Host *host = scsi_hostlist;
+    struct acornscsi_hostdata *hostdata;
+    Scsi_Device *scd;
+
+    while (host) {
+	if (host->host_no == host_no)
+	    break;
+	host = host->next;
+    }
+
+    hostdata  = (struct acornscsi_hostdata *) host->hostdata;
+    
+    if (inout == 1)
+	return -EINVAL;
+
+    begin = 0;
+    pos = sprintf(buffer,
+			"Acorn SCSI driver version %d.%02d\n", VER_MAJOR, VER_MINOR);
+
+    len = sprintf(buffer+pos,
+    			"Address: %08X          IRQ : %d\n"
+    			"SBIC   : WD33C93           DMAC: uPC71071\n\n"
+			"Statistics:\n", host->io_port, host->irq);
+    pos += len;
+    
+    len = sprintf(buffer+pos,
+			"Queued commands: %ld\n"
+			"Issued commands: %ld\n"
+			"Done commands  : %ld\n"
+			"Reads          : %ld\n"
+			"Writes         : %ld\n"
+			"Others         : %ld\n"
+			"Disconnects    : %ld\n"
+			"Aborts         : %ld\n"
+			"Resets         : %ld\n",
+				hostdata->stats.queues,
+				hostdata->stats.removes,
+				hostdata->stats.fins,
+				hostdata->stats.reads,
+				hostdata->stats.writes,
+				hostdata->stats.miscs,
+				hostdata->stats.disconnects,
+				hostdata->stats.aborts,
+				hostdata->stats.resets);
+    pos += len;
+
+    scd = scsi_devices;
+
+    first = 1;
+    
+    while (scd) {
+	if (scd->host == host) {
+    	    if (first) {
+		len = sprintf(buffer+pos,
+    			"\nAttached devices:\n");
+		pos += len;
+		first = 0;
+	    }
+
+	    proc_print_scsidevice(scd, buffer, &len, pos);
+	    pos += len;
+	    pos += sprintf (buffer+pos, "Extensions: ");
+	    if (scd->tagged_supported)
+		pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
+			scd->tagged_queue ? "en" : "dis", scd->current_tag);
+	    pos += sprintf (buffer+pos, "\n");
+
+	    if (pos + begin < offset) {
+	    	begin += pos;
+	    	pos = 0;
+	    }
+	    if (pos + begin > offset + length)
+	    	break;
+	}
+	scd = scd->next;
+    }
+
+    if (first) {
+	len = sprintf(buffer+pos,
+ 			"Attached devices: none\n");
+ 	pos += len;
+    }
+    
+    *start = buffer + (offset - begin);
+    pos -= offset - begin;
+    if (pos > length)
+	pos = length;
+
+    return pos;
+}
+
+#include "sd.h"
+#include "scsi_ioctl.h"
+
+int acornscsi_biosparam (Scsi_Disk *disk, kdev_t dev, int info_array[])
+{
+	info_array[0] = 16;
+	info_array[1] = 63;
+	info_array[2] = 2112;
+
+	return 0;
+}
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = ACORNSCSI_3;
+
+#define SCSI_CLEANUP \
+	{ int i; for (i = 0; i<4; i++) if (ecs[i]) ecard_release(ecs[i]); }
+
+#include "scsi_module.c"
+#endif
diff -urNwbB linux/arch/arm/drivers/scsi/acornscsi.h linux.arm/arch/arm/drivers/scsi/acornscsi.h
--- linux/arch/arm/drivers/scsi/acornscsi.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/acornscsi.h	Sun Feb 25 13:45:55 1996
@@ -0,0 +1,306 @@
+#ifndef ACORNSCSI_H
+#define ACORNSCSI_H
+
+#ifndef ASM
+extern int acornscsi_detect (Scsi_Host_Template *);
+extern const char *acornscsi_info (struct Scsi_Host *);
+extern int acornscsi_queuecmd (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int acornscsi_abort (Scsi_Cmnd *);
+extern int acornscsi_reset (Scsi_Cmnd *);
+extern int acornscsi_proc_info (char *, char **, off_t, int, int, int);
+extern int acornscsi_biosparam (Disk *, kdev_t, int []);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifndef PROC_SCSI_AKA30
+#include "linux/proc_fs.h"
+#define PROC_SCSI_AKA30 PROC_SCSI_EATA
+#endif
+
+#define ACORNSCSI_3 \
+	{ \
+	NULL, \
+	NULL, \
+	NULL, \
+	acornscsi_proc_info, \
+	"Acorn 16-bit SCSI", \
+	acornscsi_detect, \
+	NULL, \
+	acornscsi_info, \
+	NULL, \
+	acornscsi_queuecmd, \
+	acornscsi_abort, \
+	acornscsi_reset, \
+	NULL, \
+	acornscsi_biosparam, \
+	CAN_QUEUE, \
+	7, \
+	SG_ALL, \
+	CMD_PER_LUN, \
+	0, \
+	0, \
+	DISABLE_CLUSTERING \
+	}
+
+#ifndef HOSTS_C
+
+/* SBIC registers */
+#define OWNID			0
+#define OWNID_FS1		(1<<7)
+#define OWNID_FS2		(1<<6)
+#define OWNID_EHP		(1<<4)
+#define OWNID_EAF		(1<<3)
+
+#define CTRL			1
+#define CTRL_DMAMODE		(1<<7)
+#define CTRL_DMADBAMODE		(1<<6)
+#define CTRL_DMABURST		(1<<5)
+#define CTRL_DMAPOLLED		0
+#define CTRL_HHP		(1<<4)
+#define CTRL_EDI		(1<<3)
+#define CTRL_IDI		(1<<2)
+#define CTRL_HA			(1<<1)
+#define CTRL_HSP		(1<<0)
+
+#define TIMEOUT			2
+#define TOTSECTS		3
+#define TOTHEADS		4
+#define TOTCYLH			5
+#define TOTCYLL			6
+#define LOGADDRH		7
+#define LOGADDRM2		8
+#define LOGADDRM1		9
+#define LOGADDRL		10
+#define SECTORNUM		11
+#define HEADNUM			12
+#define CYLH			13
+#define CYLL			14
+#define TARGETLUN		15
+#define TARGETLUN_TLV		(1<<7)
+#define TARGETLUN_DOK		(1<<6)
+
+#define CMNDPHASE		16
+#define SYNCHTRANSFER		17
+#define SYNCHTRANSFER_OF0	0x00
+#define SYNCHTRANSFER_OF1	0x01
+#define SYNCHTRANSFER_OF2	0x02
+#define SYNCHTRANSFER_OF3	0x03
+#define SYNCHTRANSFER_OF4	0x04
+#define SYNCHTRANSFER_OF5	0x05
+#define SYNCHTRANSFER_OF6	0x06
+#define SYNCHTRANSFER_OF7	0x07
+#define SYNCHTRANSFER_OF8	0x08
+#define SYNCHTRANSFER_OF9	0x09
+#define SYNCHTRANSFER_OF10	0x0A
+#define SYNCHTRANSFER_OF11	0x0B
+#define SYNCHTRANSFER_OF12	0x0C
+#define SYNCHTRANSFER_8DBA	0x00
+#define SYNCHTRANSFER_2DBA	0x20
+#define SYNCHTRANSFER_3DBA	0x30
+#define SYNCHTRANSFER_4DBA	0x40
+#define SYNCHTRANSFER_5DBA	0x50
+#define SYNCHTRANSFER_6DBA	0x60
+#define SYNCHTRANSFER_7DBA	0x70
+
+#define TRANSCNTH		18
+#define TRANSCNTM		19
+#define TRANSCNTL		20
+#define DESTID			21
+#define DESTID_SCC		(1<<7)
+#define DESTID_DPD		(1<<6)
+
+#define SOURCEID		22
+#define SOURCEID_ER		(1<<7)
+#define SOURCEID_ES		(1<<6)
+#define SOURCEID_DSP		(1<<5)
+#define SOURCEID_SIV		(1<<4)
+
+#define SSR			23
+#define CMND			24
+#define CMND_RESET		0x00
+#define CMND_ABORT		0x01
+#define CMND_ASSERTATN		0x02
+#define CMND_NEGATEACK		0x03
+#define CMND_DISCONNECT		0x04
+#define CMND_RESELECT		0x05
+#define CMND_SELWITHATN		0x06
+#define CMND_SELECT		0x07
+#define CMND_SELECTATNTRANSFER	0x08
+#define CMND_SELECTTRANSFER	0x09
+#define CMND_RESELECTRXDATA	0x0A
+#define CMND_RESELECTTXDATA	0x0B
+#define CMND_WAITFORSELRECV	0x0C
+#define CMND_SENDSTATCMD	0x0D
+#define CMND_SENDDISCONNECT	0x0E
+#define CMND_SETIDI		0x0F
+#define CMND_RECEIVECMD		0x10
+#define CMND_RECEIVEDTA		0x11
+#define CMND_RECEIVEMSG		0x12
+#define CMND_RECEIVEUSP		0x13
+#define CMND_SENDCMD		0x14
+#define CMND_SENDDATA		0x15
+#define CMND_SENDMSG		0x16
+#define CMND_SENDUSP		0x17
+#define CMND_TRANSLATEADDR	0x18
+#define CMND_XFERINFO		0x20
+#define CMND_SBT		(1<<7)
+
+#define DATA			25
+#define ASR			26
+#define ASR_INT			(1<<7)
+#define ASR_LCI			(1<<6)
+#define ASR_BSY			(1<<5)
+#define ASR_CIP			(1<<4)
+#define ASR_PE			(1<<1)
+#define ASR_DBR			(1<<0)
+
+/* DMAC registers */
+#define INIT			0x00
+#define INIT_8BIT		(1)
+
+#define CHANNEL			0x80
+#define CHANNEL_0		0x00
+#define CHANNEL_1		0x01
+#define CHANNEL_2		0x02
+#define CHANNEL_3		0x03
+
+#define TXCNTLO			0x01
+#define TXCNTHI			0x81
+#define TXADRLO			0x02
+#define TXADRMD			0x82
+#define TXADRHI			0x03
+
+#define DEVCON0			0x04
+#define DEVCON0_AKL		(1<<7)
+#define DEVCON0_RQL		(1<<6)
+#define DEVCON0_EXW		(1<<5)
+#define DEVCON0_ROT		(1<<4)
+#define DEVCON0_CMP		(1<<3)
+#define DEVCON0_DDMA		(1<<2)
+#define DEVCON0_AHLD		(1<<1)
+#define DEVCON0_MTM		(1<<0)
+
+#define DEVCON1			0x84
+#define DEVCON1_WEV		(1<<1)
+#define DEVCON1_BHLD		(1<<0)
+
+#define MODECON			0x05
+#define MODECON_WOED		0x01
+#define MODECON_VERIFY		0x00
+#define MODECON_READ		0x04
+#define MODECON_WRITE		0x08
+#define MODECON_AUTOINIT	0x10
+#define MODECON_ADDRDIR		0x20
+#define MODECON_DEMAND		0x00
+#define MODECON_SINGLE		0x40
+#define MODECON_BLOCK		0x80
+#define MODECON_CASCADE		0xC0
+
+#define STATUS			0x85
+#define TEMPLO			0x06
+#define TEMPHI			0x86
+#define REQREG			0x07
+#define MASKREG			0x87
+#define MASKREG_M0		0x01
+#define MASKREG_M1		0x02
+#define MASKREG_M2		0x04
+#define MASKREG_M3		0x08
+
+/* miscellaneous internal variables */
+
+#define CMD_READ		0
+#define CMD_WRITE		1
+#define CMD_MISC		2
+
+#define PHASE_IDLE			0x00
+#define PHASE_CONNECTING		0x41
+#define PHASE_CONNECTED			0x42
+#define PHASE_COMMAND			0x43
+#define PHASE_COMMANDSENT		0x44
+
+#define PHASE_DATAIN			0x46
+#define PHASE_DATAOUT			0x47
+
+#define PHASE_STATUSIN			0x4c
+#define PHASE_FINISHED			0x4d
+#define PHASE_DISCONNECTING		0x4f
+#define PHASE_RECONNECTED		0x50
+
+#define PHASE_CONNECTING_FOR_ABORT	0x80
+#define PHASE_CONNECTED_FOR_ABORT	0x81
+
+#define INTR_CONNECTED		0
+#define INTR_NOTCONNECTED	1
+#define INTR_CONTINUE		2
+
+struct as_stats {
+    unsigned long queues;
+    unsigned long removes;
+    unsigned long fins;
+    unsigned long reads;
+    unsigned long writes;
+    unsigned long miscs;
+    unsigned long aborts;
+    unsigned long resets;
+    unsigned long disconnects;
+};
+
+struct as_cmnd {
+    Scsi_Cmnd *cmd;
+    struct as_cmnd *next;				/* next command in queue		 */
+    int used;						/* block in use				 */
+    unsigned long queued_jiffies;
+    unsigned long connect_jiffies;
+    unsigned long totallen;
+    unsigned long buf0addr;
+    unsigned long buf1addr;
+    unsigned long saved_dmac_address;
+    unsigned long saved_dmac_transcnt;
+    unsigned long saved_sbic_transcnt;
+    unsigned short buf0len;
+    unsigned short buf1len;
+    unsigned char buffer;
+    unsigned char saved;
+};
+
+struct as_dev {
+    struct as_cmnd *issue_head;
+    struct as_cmnd *issue_tail;
+    struct as_cmnd *disconnected;			/* commands disconnected		 */
+    unsigned char busy;					/* luns on device that are busy		 */
+};
+    
+struct acornscsi_hostdata {
+    int busy;						/* in process of connecting		 */
+    int page_reg;					/* current page register value		 */
+
+    /* data for the connected command */
+    int phase;						/* phase of command			 */
+    int last_message;					/* last message sent			 */
+    int msgout;						/* message to be sent out		 */
+    unsigned long tmpsbiclen;
+
+    struct as_cmnd *connecting;				/* command that is connecting		 */
+    struct as_cmnd *aborting;				/* command that is aborting		 */
+    struct as_cmnd *connected;				/* command that is connected		 */
+
+    struct as_cmnd queuedcommands[CAN_QUEUE];		/* commands 				 */
+
+    struct as_dev dev[8];
+    struct as_stats stats;
+};
+
+#endif
+
+#endif /* ndef ASM */
+#endif /* ACORNSCSI_H */
diff -urNwbB linux/arch/arm/drivers/scsi/cumana_1.c linux.arm/arch/arm/drivers/scsi/cumana_1.c
--- linux/arch/arm/drivers/scsi/cumana_1.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/cumana_1.c	Sat Feb 24 09:36:55 1996
@@ -0,0 +1,318 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking.  Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing.  Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect.  Untested.
+ */
+
+/*
+ * $Log: cumana_NCR5380.c,v $
+ */
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "cumana_1.h"
+#include "NCR5380.h"
+#include "constants.h"
+
+#include <asm/ecard.h>
+
+static unsigned int cumana_1_prods[] = { 0x00a0 };
+static unsigned int cumana_1_manus[] = { 0x003a };
+
+/*
+ * Function : cumana_NCR5380_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ *	equal to the number of ints.
+ *
+ */
+
+void cumana_NCR5380_setup(char *str, int *ints) {
+}
+
+/*
+ * Function : int cumana_NCR5380_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes cumana NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int cumana_NCR5380_detect(Scsi_Host_Template * tpnt) {
+    struct expansion_card *ec;
+    int count = 0;
+    struct Scsi_Host *instance;
+
+    while(1) {
+    	if((ec = ecard_find(0, sizeof(cumana_1_prods), cumana_1_prods, cumana_1_manus)) == NULL)
+    		break;
+
+        instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+        instance->io_port = (((int)ec->r_podaddr & ~0x003c0000UL) | 0x00242000UL) >> 2;
+
+	NCR5380_init(instance, 0);
+
+	instance->irq = ec->irq;
+        ((struct NCR5380_hostdata *)instance->hostdata)->ctrl=0;
+        outb(0x00, instance->io_port - 577);
+
+	if (instance->irq != IRQ_NONE)
+	    if (request_irq(instance->irq, cumana_NCR5380_intr, SA_INTERRUPT, "Cumana SCSI 1 card")) {
+		printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+		    instance->host_no, instance->irq);
+		instance->irq = IRQ_NONE;
+	    }
+
+	if (instance->irq == IRQ_NONE) {
+	    printk("scsi%d: interrupts not enabled. for better interactive performance,\n", instance->host_no);
+	    printk("scsi%d: please jumper the board for a free IRQ.\n", instance->host_no);
+	}
+
+	printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+	if (instance->irq == IRQ_NONE)
+	    printk ("s disabled");
+	else
+	    printk (" %d", instance->irq);
+	printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+	    CAN_QUEUE, CMD_PER_LUN, CUMANA_NCR5380_PUBLIC_RELEASE);
+	printk("\nscsi%d:", instance->host_no);
+	NCR5380_print_options(instance);
+	printk("\n");
+
+	ecard_claim(ec);
+	++count;
+    }
+    return count;
+}
+
+const char * cumana_NCR5380_info (struct Scsi_Host *spnt) {
+    return "";
+}
+
+#ifdef NOT_EFFICIENT
+#define CTRL(p,v)     outb(*ctrl = (v), (p) - 577)
+#define STAT(p)       inb((p)+1)
+#define IN(p)         inb((p))
+#define OUT(v,p)      outb((v), (p))
+#else
+#define CTRL(p,v)	(p[-2308] = (*ctrl = (v)))
+#define STAT(p)		(p[4])
+#define IN(p)		(*(p))
+#define IN2(p)		((unsigned short)(*(volatile unsigned long *)(p)))
+#define OUT(v,p)	(*(p) = (v))
+#define OUT2(v,p)	(*((volatile unsigned long *)(p)) = (v))
+#endif
+#define L(v)		(((v)<<16)|((v) & 0x0000ffff))
+#define H(v)		(((v)>>16)|((v) & 0xffff0000))
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+  int oldctrl = *ctrl;
+  unsigned long *laddr;
+#ifdef NOT_EFFICIENT
+  int ioaddr = instance->io_port;
+  int dma_io = ioaddr & ~(0x3C0000>>2);
+#else
+  volatile unsigned char *ioaddr = (unsigned char *)(instance->io_port<<2);
+  volatile unsigned char *dma_io = (unsigned char *)((int)ioaddr & ~0x3C0000);
+#endif
+
+  if(!len) return 0;
+
+  CTRL(ioaddr, 0x02);
+  laddr = (unsigned long *)addr;
+  while(len >= 32)
+  {
+    int status;
+    unsigned long v;
+    status = STAT(ioaddr);
+    if(status & 0x80)
+      goto end;
+    if(!(status & 0x40))
+      continue;
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    len -= 32;
+    if(len == 0)
+      break;
+  }
+
+  addr = (unsigned char *)laddr;
+  CTRL(ioaddr, 0x12);
+  while(len > 0)
+  {
+    int status;
+    status = STAT(ioaddr);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      OUT(*addr++, dma_io);
+      if(--len == 0)
+        break;
+    }
+
+    status = STAT(ioaddr);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      OUT(*addr++, dma_io);
+      if(--len == 0)
+        break;
+    }
+  }
+end:
+  CTRL(ioaddr, oldctrl|0x40);
+  return len;
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+  int oldctrl = *ctrl;
+  unsigned long *laddr;
+#ifdef NOT_EFFICIENT
+  int ioaddr = instance->io_port;
+  int dma_io = ioaddr & ~(0x3C0000>>2);
+#else
+  volatile unsigned char *ioaddr = (unsigned char *)(instance->io_port<<2);
+  volatile unsigned char *dma_io = (unsigned char *)((int)ioaddr & ~0x3C0000);
+#endif
+
+  if(!len) return 0;
+
+  CTRL(ioaddr, 0x00);
+  laddr = (unsigned long *)addr;
+  while(len >= 32)
+  {
+    int status;
+    status = STAT(ioaddr);
+    if(status & 0x80)
+      goto end;
+    if(!(status & 0x40))
+      continue;
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    len -= 32;
+    if(len == 0)
+      break;
+  }
+
+  addr = (unsigned char *)laddr;
+  CTRL(ioaddr, 0x10);
+  while(len > 0)
+  {
+    int status;
+    status = STAT(ioaddr);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      *addr++ = IN(dma_io);
+      if(--len == 0)
+        break;
+    }
+
+    status = STAT(ioaddr);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      *addr++ = IN(dma_io);
+      if(--len == 0)
+        break;
+    }
+  }
+end:
+  CTRL(ioaddr, oldctrl|0x40);
+  return len;
+}
+
+#undef STAT
+#undef CTRL
+#undef IN
+#undef OUT
+
+#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
+
+static char cumana_NCR5380_read(struct Scsi_Host *instance, int reg)
+{
+  int ioaddr = instance->io_port;
+  int i;
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+  CTRL(ioaddr, 0);
+  i = inb(ioaddr + 64 + reg);
+  CTRL(ioaddr, 0x40);
+
+  return i;
+}
+
+static void cumana_NCR5380_write(struct Scsi_Host *instance, int reg, int value)
+{
+  int ioaddr = instance->io_port;
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+  CTRL(ioaddr, 0);
+  outb(value, ioaddr + 64 + reg);
+  CTRL(ioaddr, 0x40);
+}
+
+#undef CTRL
+
+#include "NCR5380.c"
diff -urNwbB linux/arch/arm/drivers/scsi/cumana_1.h linux.arm/arch/arm/drivers/scsi/cumana_1.h
--- linux/arch/arm/drivers/scsi/cumana_1.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/cumana_1.h	Sat Feb 24 09:36:55 1996
@@ -0,0 +1,88 @@
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix and Linux consulting and custom programming)
+ *	drew@colorado.edu
+ *      +1 (303) 440-4894
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: cumana_NCR5380.h,v $
+ */
+
+#ifndef CUMANA_NCR5380_H
+#define CUMANA_NCR5380_H
+
+#define CUMANA_NCR5380_PUBLIC_RELEASE 1
+
+
+#ifndef ASM
+int cumana_NCR5380_abort(Scsi_Cmnd *);
+int cumana_NCR5380_detect(Scsi_Host_Template *);
+const char *cumana_NCR5380_info(struct Scsi_Host *);
+int cumana_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int cumana_NCR5380_reset(Scsi_Cmnd *);
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifdef HOSTS_C
+
+#define CUMANA_NCR5380 {NULL, NULL, "Cumana 16-bit SCSI",		\
+	cumana_NCR5380_detect, NULL, cumana_NCR5380_info, NULL, 	\
+	cumana_NCR5380_queue_command, cumana_NCR5380_abort, 		\
+	cumana_NCR5380_reset, NULL, 					\
+	NULL, /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL,		\
+	/* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#else
+#define NCR5380_implementation_fields \
+    int port, ctrl
+
+#define NCR5380_local_declare() \
+        struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+        _instance = instance
+
+#define NCR5380_read(reg) cumana_NCR5380_read(_instance, reg)
+#define NCR5380_write(reg, value) cumana_NCR5380_write(_instance, reg, value)
+
+#define NCR5380_intr cumana_NCR5380_intr
+#define NCR5380_queue_command cumana_NCR5380_queue_command
+#define NCR5380_abort cumana_NCR5380_abort
+#define NCR5380_reset cumana_NCR5380_reset
+
+#define BOARD_NORMAL	0
+#define BOARD_NCR53C400	1
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* CUMANA_NCR5380_H */
+
diff -urNwbB linux/arch/arm/drivers/scsi/ecoscsi.c linux.arm/arch/arm/drivers/scsi/ecoscsi.c
--- linux/arch/arm/drivers/scsi/ecoscsi.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/ecoscsi.c	Sat Feb 24 09:36:55 1996
@@ -0,0 +1,207 @@
+#define AUTOSENSE
+/* #define PSEUDO_DMA */
+
+/*
+ * EcoSCSI Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking.  Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing.  Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect.  Untested.
+ */
+
+/*
+ * $Log: ecoscsi_NCR5380.c,v $
+ */
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "ecoscsi.h"
+#include "NCR5380.h"
+#include "constants.h"
+
+static char ecoscsi_NCR5380_read(struct Scsi_Host *instance, int reg)
+{
+  int ioaddr = instance->io_port;
+  outb(reg | 8, ioaddr);
+  return inb(ioaddr + 1);
+}
+
+static void ecoscsi_NCR5380_write(struct Scsi_Host *instance, int reg, int value)
+{
+  int ioaddr = instance->io_port;
+  outb(reg | 8, ioaddr);
+  outb(value, ioaddr + 1);
+}
+
+/*
+ * Function : ecoscsi_NCR5380_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ *	equal to the number of ints.
+ *
+ */
+
+void ecoscsi_NCR5380_setup(char *str, int *ints) {
+}
+
+/*
+ * Function : int ecoscsi_NCR5380_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes ecoscsi NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int ecoscsi_NCR5380_detect(Scsi_Host_Template * tpnt) {
+    struct Scsi_Host *instance;
+
+    instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+    instance->io_port = 0x033A0000 >> 2;
+
+    ecoscsi_NCR5380_write (instance, MODE_REG, 0x20);		/* Is it really SCSI? */
+    if (ecoscsi_NCR5380_read (instance, MODE_REG) != 0x20)	/* Write to a reg.    */
+    {
+        scsi_unregister(instance);
+        return 0;						/* and try to read    */
+    }
+    ecoscsi_NCR5380_write( instance, MODE_REG, 0x00 );		/* it back.	      */
+    if (ecoscsi_NCR5380_read (instance, MODE_REG) != 0x00)
+    {
+        scsi_unregister(instance);
+        return 0;
+    }
+
+    NCR5380_init(instance, 0);
+
+    instance->irq = IRQ_NONE;
+
+    if (instance->irq != IRQ_NONE)
+	if (request_irq(instance->irq, ecoscsi_NCR5380_intr, SA_INTERRUPT, "NCR5380")) {
+	    printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+	    instance->host_no, instance->irq);
+	    instance->irq = IRQ_NONE;
+	}
+
+    if (instance->irq != IRQ_NONE) {
+  	printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
+	printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
+    }
+
+    printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+    if (instance->irq == IRQ_NONE)
+	printk ("s disabled");
+    else
+        printk (" %d", instance->irq);
+    printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+        CAN_QUEUE, CMD_PER_LUN, ECOSCSI_NCR5380_PUBLIC_RELEASE);
+    printk("\nscsi%d:", instance->host_no);
+    NCR5380_print_options(instance);
+    printk("\n");
+    return 1;
+}
+
+const char * ecoscsi_NCR5380_info (struct Scsi_Host *spnt) {
+    return "";
+}
+
+#if 0
+#define STAT(p) inw(p + 144)
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int ioaddr = instance->io_port;
+printk("writing %p len %d\n",addr, len);
+  if(!len) return -1;
+
+  while(1)
+  {
+    int status;
+    while(((status = STAT(ioaddr)) & 0x100)==0);
+  }
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int ioaddr = instance->io_port;
+  int ioaddr2= instance->io_port + 0x100;
+  unsigned char *start = addr;
+  int s;
+printk("reading %p len %d\n",addr, len);
+  outb(inb(ioaddr + 128), ioaddr + 135);
+  while(len > 0)
+  {
+    int status,b,i, timeout;
+    timeout = 0x07FFFFFF;
+    while(((status = STAT(ioaddr)) & 0x100)==0)
+    {
+      timeout--;
+      if(status & 0x200 || !timeout)
+      {
+        printk("status = %p\n",status);
+        outb(0, ioaddr + 135);
+        return 1;
+      }
+    }
+    if(len >= 128)
+    {
+      for(i=0; i<64; i++)
+      {
+        b = inw(ioaddr + 136);
+        *addr++ = b;
+        *addr++ = b>>8;
+      }
+      len -= 128;
+    }
+    else
+    {
+      b = inw(ioaddr + 136);
+      *addr ++ = b;
+      len -= 1;
+      if(len)
+        *addr ++ = b>>8;
+      len -= 1;
+    }
+  }
+  outb(0, ioaddr + 135);
+  printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]);
+  return 1;
+}
+#endif
+#undef STAT
+
+#include "NCR5380.c"
diff -urNwbB linux/arch/arm/drivers/scsi/ecoscsi.h linux.arm/arch/arm/drivers/scsi/ecoscsi.h
--- linux/arch/arm/drivers/scsi/ecoscsi.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/ecoscsi.h	Sat Feb 24 09:36:55 1996
@@ -0,0 +1,84 @@
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: ecoscsi_NCR5380.h,v $
+ */
+
+#ifndef ECOSCSI_NCR5380_H
+#define ECOSCSI_NCR5380_H
+
+#define ECOSCSI_NCR5380_PUBLIC_RELEASE 1
+
+
+#ifndef ASM
+int ecoscsi_NCR5380_abort(Scsi_Cmnd *);
+int ecoscsi_NCR5380_detect(Scsi_Host_Template *);
+const char *ecoscsi_NCR5380_info(struct Scsi_Host *);
+int ecoscsi_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int ecoscsi_NCR5380_reset(Scsi_Cmnd *);
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifdef HOSTS_C
+
+#define ECOSCSI_NCR5380 {NULL, NULL, "Serial Port EcoSCSI NCR5380",	\
+	ecoscsi_NCR5380_detect, NULL, ecoscsi_NCR5380_info, NULL, 	\
+	ecoscsi_NCR5380_queue_command, ecoscsi_NCR5380_abort, 		\
+	ecoscsi_NCR5380_reset, NULL, 					\
+	NULL, /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL,		\
+	/* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#else
+#define NCR5380_implementation_fields \
+    int port, ctrl
+
+#define NCR5380_local_declare() \
+        struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+        _instance = instance
+
+#define NCR5380_read(reg) ecoscsi_NCR5380_read(_instance, reg)
+#define NCR5380_write(reg, value) ecoscsi_NCR5380_write(_instance, reg, value)
+
+#define NCR5380_intr ecoscsi_NCR5380_intr
+#define NCR5380_queue_command ecoscsi_NCR5380_queue_command
+#define NCR5380_abort ecoscsi_NCR5380_abort
+#define NCR5380_reset ecoscsi_NCR5380_reset
+
+#define BOARD_NORMAL	0
+#define BOARD_NCR53C400	1
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* ECOSCSI_NCR5380_H */
+
diff -urNwbB linux/arch/arm/drivers/scsi/hosts.c linux.arm/arch/arm/drivers/scsi/hosts.c
--- linux/arch/arm/drivers/scsi/hosts.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/hosts.c	Sat Feb 24 09:36:56 1996
@@ -0,0 +1,339 @@
+/*
+ *  hosts.c Copyright (C) 1992 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>
+ */
+
+
+/*
+ *  This file contains the medium level SCSI
+ *  host interface initialization, as well as the scsi_hosts array of SCSI
+ *  hosts currently present in the system.
+ */
+
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/config.h>
+#include "../block/blk.h"
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+
+#include "scsi.h"
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#define HOSTS_C
+
+#include "hosts.h"
+
+#ifdef CONFIG_SCSI_ACORNSCSI_3
+#include "acornscsi.h"
+#endif
+
+#ifdef CONFIG_SCSI_CUMANA_1
+#include "cumana_1.h"
+#endif
+
+#ifdef CONFIG_SCSI_OAK1
+#include "oak.h"
+#endif
+
+#ifdef CONFIG_SCSI_ECOSCSI
+#include "ecoscsi.h"
+#endif
+
+#ifdef CONFIG_SCSI_DEBUG
+#include "scsi_debug.h"
+#endif
+
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.c,v 1.3 1993/09/24 12:21:00 drew Exp drew $";
+*/
+
+/*
+ *  The scsi host entries should be in the order you wish the
+ *  cards to be detected.  A driver may appear more than once IFF
+ *  it can deal with being detected (and therefore initialized)
+ *  with more than one simultaneous host number, can handle being
+ *  reentrant, etc.
+ *
+ *  They may appear in any order, as each SCSI host is told which host 
+ *  number it is during detection.
+ */
+
+/* This is a placeholder for controllers that are not configured into
+ * the system - we do this to ensure that the controller numbering is
+ * always consistent, no matter how the kernel is configured. */
+
+#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
+			   NULL, NULL, 0, 0, 0, 0, 0, 0}
+
+/*
+ *  When figure is run, we don't want to link to any object code.  Since
+ *  the macro for each host will contain function pointers, we cannot
+ *  use it and instead must use a "blank" that does no such
+ *  idiocy.
+ */
+
+Scsi_Host_Template * scsi_hosts = NULL;
+
+static Scsi_Host_Template builtin_scsi_hosts[] =
+{
+#ifdef CONFIG_SCSI_ACORNSCSI_3
+    ACORNSCSI_3,
+#endif
+#ifdef CONFIG_SCSI_CUMANA_1
+    CUMANA_NCR5380,
+#endif
+#ifdef CONFIG_SCSI_OAK1
+    OAK_NCR5380,
+#endif
+#ifdef CONFIG_SCSI_ECOSCSI
+    ECOSCSI_NCR5380,
+#endif
+#ifdef CONFIG_SCSI_DEBUG
+    SCSI_DEBUG,
+#endif
+};
+
+#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
+
+
+/*
+ *  Our semaphores and timeout counters, where size depends on 
+ *      MAX_SCSI_HOSTS here.
+ */
+
+struct Scsi_Host * scsi_hostlist = NULL;
+struct Scsi_Device_Template * scsi_devicelist = NULL;
+
+int max_scsi_hosts = 0;
+int next_scsi_host = 0;
+
+void
+scsi_unregister(struct Scsi_Host * sh){
+    struct Scsi_Host * shpnt;
+    
+    if(scsi_hostlist == sh)
+	scsi_hostlist = sh->next;
+    else {
+	shpnt = scsi_hostlist;
+	while(shpnt->next != sh) shpnt = shpnt->next;
+	shpnt->next = shpnt->next->next;
+    }
+    
+    /* If we are removing the last host registered, it is safe to reuse
+     * its host number (this avoids "holes" at boot time) (DB) 
+     */
+    if (max_scsi_hosts == next_scsi_host && !scsi_loadable_module_flag)
+	max_scsi_hosts--;
+    
+    next_scsi_host--;
+    scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
+}
+
+/* We call this when we come across a new host adapter. We only do this
+ * once we are 100% sure that we want to use this host adapter -  it is a
+ * pain to reverse this, so we try and avoid it 
+ */
+
+struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
+    struct Scsi_Host * retval, *shpnt;
+    retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
+						  (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
+    retval->host_busy = 0;
+    retval->block = NULL;
+    retval->wish_block = 0;
+    if(j > 0xffff) panic("Too many extra bytes requested\n");
+    retval->extra_bytes = j;
+    retval->loaded_as_module = scsi_loadable_module_flag;
+    retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
+    next_scsi_host++;
+    retval->host_queue = NULL;
+    retval->host_wait = NULL;
+    retval->last_reset = 0;
+    retval->irq = 0;
+    retval->dma_channel = 0xff;
+
+    /* These three are default values which can be overridden */
+    retval->max_channel = 0; 
+    retval->max_id = 8;      
+    retval->max_lun = 8;
+
+    retval->unique_id = 0;
+    retval->io_port = 0;
+    retval->forbidden_addr = 0;
+    retval->forbidden_size = 0;
+    retval->hostt = tpnt;
+    retval->next = NULL;
+#ifdef DEBUG
+    printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
+#endif
+
+    /* The next six are the default values which can be overridden
+     * if need be */
+    retval->this_id = tpnt->this_id;
+    retval->can_queue = tpnt->can_queue;
+    retval->sg_tablesize = tpnt->sg_tablesize;
+    retval->cmd_per_lun = tpnt->cmd_per_lun;
+    retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
+    retval->use_clustering = tpnt->use_clustering;   
+    if(!scsi_hostlist)
+	scsi_hostlist = retval;
+    else
+    {
+	shpnt = scsi_hostlist;
+	while(shpnt->next) shpnt = shpnt->next;
+	shpnt->next = retval;
+    }
+    
+    return retval;
+}
+
+int
+scsi_register_device(struct Scsi_Device_Template * sdpnt)
+{
+    if(sdpnt->next) panic("Device already registered");
+    sdpnt->next = scsi_devicelist;
+    scsi_devicelist = sdpnt;
+    return 0;
+}
+
+unsigned int scsi_init()
+{
+    static int called = 0;
+    int i, pcount;
+    Scsi_Host_Template * tpnt;
+    struct Scsi_Host * shpnt;
+    const char * name;
+    
+    if(called) return 0;
+
+    called = 1;
+    for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
+    {
+	/*
+	 * Initialize our semaphores.  -1 is interpreted to mean
+	 * "inactive" - where as 0 will indicate a time out condition.
+	 */
+	
+	pcount = next_scsi_host;
+	if ((tpnt->detect) &&
+	    (tpnt->present =
+	     tpnt->detect(tpnt)))
+	{
+	    /* The only time this should come up is when people use
+	     * some kind of patched driver of some kind or another. */
+	    if(pcount == next_scsi_host) {
+		if(tpnt->present > 1)
+		    panic("Failure to register low-level scsi driver");
+		/* The low-level driver failed to register a driver.  We
+		 * can do this now. */
+		scsi_register(tpnt,0);
+	    }
+	    tpnt->next = scsi_hosts;
+	    scsi_hosts = tpnt;
+
+	     /* Add the driver to /proc/scsi */
+#if CONFIG_PROC_FS 
+	     build_proc_dir_entries(tpnt);
+#endif
+	}
+    }
+        
+    for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+    {
+	if(shpnt->hostt->info)
+	    name = shpnt->hostt->info(shpnt);
+	else
+	    name = shpnt->hostt->name;
+	printk ("scsi%d : %s\n", /* And print a little message */
+		shpnt->host_no, name);
+    }
+    
+    printk ("scsi : %d host%s.\n", next_scsi_host,
+	    (next_scsi_host == 1) ? "" : "s");
+    
+    scsi_make_blocked_list();
+    
+    /* Now attach the high level drivers */
+#ifdef CONFIG_BLK_DEV_SD
+    scsi_register_device(&sd_template);
+#endif
+#ifdef CONFIG_BLK_DEV_SR
+    scsi_register_device(&sr_template);
+#endif
+#ifdef CONFIG_CHR_DEV_ST
+    scsi_register_device(&st_template);
+#endif
+#ifdef CONFIG_CHR_DEV_SG
+    scsi_register_device(&sg_template);
+#endif
+
+#if 0      
+    max_scsi_hosts = next_scsi_host;
+#endif
+    return 0;
+}
+
+
+void scsi_mem_init(unsigned long memory_end)
+{
+    struct Scsi_Host *Host;
+    long High8, Low24;
+    for (Host = scsi_hostlist; Host != NULL; Host = Host->next) {
+	if (Host->forbidden_addr > 0 && Host->forbidden_size > 0) {
+	    for (High8 = 1<<24; High8 < memory_end; High8 += 1<<24) {
+		for (Low24 = Host->forbidden_addr;
+		     Low24 < Host->forbidden_addr + Host->forbidden_size;
+		     Low24 += PAGE_SIZE) {
+		    unsigned long ForbiddenAddress = High8 + Low24;
+		    if (ForbiddenAddress >= memory_end) goto next_host;
+		    mem_map[MAP_NR(ForbiddenAddress)].reserved = 1;
+		}
+	    }
+	}
+    next_host:
+	continue;
+    }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff -urNwbB linux/arch/arm/drivers/scsi/oak.c linux.arm/arch/arm/drivers/scsi/oak.c
--- linux/arch/arm/drivers/scsi/oak.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/oak.c	Sat Feb 24 09:36:56 1996
@@ -0,0 +1,209 @@
+#define AUTOSENSE
+/*#define PSEUDO_DMA*/
+
+/*
+ * Oak Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking.  Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing.  Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect.  Untested.
+ */
+
+/*
+ * $Log: oak.c,v $
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/config.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "oak.h"
+#include "NCR5380.h"
+#include "constants.h"
+
+#undef START_DMA_INITIATOR_RECEIVE_REG
+#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128)
+
+#include <asm/ecard.h>
+
+static int oak_prods[] = { 0x0058 };
+static int oak_manus[] = { 0x0021 };
+
+/*
+ * Function : int oak_NCR5380_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes oak NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+static struct expansion_card *ecs[4];
+
+int oak_NCR5380_detect(Scsi_Host_Template * tpnt) {
+    int count = 0, i;
+    struct Scsi_Host *instance;
+
+    while(1) {
+        if ((ecs[count] = ecard_find(0, sizeof(oak_prods), oak_prods, oak_manus)) == NULL)
+            break;
+
+        instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+        instance->io_port = ((int)ecs[count]->r_podaddr & ~0x003c0000)>>2;
+
+	NCR5380_init(instance, 0);
+
+	instance->irq = 255;
+
+	if (instance->irq != IRQ_NONE)
+	    if (request_irq(instance->irq, oak_NCR5380_intr, SA_INTERRUPT, "Oak SCSI")) {
+		printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+		    instance->host_no, instance->irq);
+		instance->irq = IRQ_NONE;
+	    }
+
+	if (instance->irq != IRQ_NONE) {
+	    printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
+	    printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
+	}
+
+	printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+	if (instance->irq == IRQ_NONE)
+	    printk ("s disabled");
+	else
+	    printk (" %d", instance->irq);
+	printk(" options CAN_QUEUE=%d  CMD_PER_LUN=%d release=%d",
+	    CAN_QUEUE, CMD_PER_LUN, OAK_NCR5380_PUBLIC_RELEASE);
+	printk("\nscsi%d:", instance->host_no);
+	NCR5380_print_options(instance);
+	printk("\n");
+
+	ecard_claim(ecs[count]);
+	++count;
+    }
+    for(i=0; i<count; i++)
+    	ecard_release(ecs[i]);
+#ifdef MODULE
+    if(count == 0)
+        printk("No oak scsi devices found\n");
+#endif
+    return count;
+}
+
+const char * oak_NCR5380_info (struct Scsi_Host *spnt) {
+    return "";
+}
+
+#define STAT(p)   inw(p + 144)
+extern void inswb(int from, void *to, int len);
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int ioaddr = instance->io_port;
+printk("writing %p len %d\n",addr, len);
+  if(!len) return -1;
+
+  while(1)
+  {
+    int status;
+    while(((status = STAT(ioaddr)) & 0x100)==0);
+  }
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int ioaddr = instance->io_port;
+printk("reading %p len %d\n", addr, len);
+  while(len > 0)
+  {
+    int status, timeout;
+    unsigned long b;
+    
+    timeout = 0x01FFFFFF;
+    
+    while(((status = STAT(ioaddr)) & 0x100)==0)
+    {
+      timeout--;
+      if(status & 0x200 || !timeout)
+      {
+        printk("status = %08lX\n",status);
+        return 1;
+      }
+    }
+    if(len >= 128)
+    {
+      inswb(ioaddr + 136, addr, 128);
+      addr += 128;
+      len -= 128;
+    }
+    else
+    {
+      b = (unsigned long) inw(ioaddr + 136);
+      *addr ++ = b;
+      len -= 1;
+      if(len)
+        *addr ++ = b>>8;
+      len -= 1;
+    }
+  }
+  return 0;
+}
+
+static char oak_NCR5380_read(struct Scsi_Host *instance, int reg)
+{
+  int ioaddr = instance->io_port;
+  return inb(ioaddr + reg);
+}
+
+static void oak_NCR5380_write(struct Scsi_Host *instance, int reg, int value)
+{
+  int ioaddr = instance->io_port;
+  outb(value, ioaddr + reg);
+}
+
+#undef STAT
+
+#include "NCR5380.c"
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = OAK_NCR5380;
+
+#include "scsi_module.c"
+#endif
+
diff -urNwbB linux/arch/arm/drivers/scsi/oak.h linux.arm/arch/arm/drivers/scsi/oak.h
--- linux/arch/arm/drivers/scsi/oak.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/oak.h	Sat Feb 24 09:36:56 1996
@@ -0,0 +1,86 @@
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix and Linux consulting and custom programming)
+ *	drew@colorado.edu
+ *      +1 (303) 440-4894
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: oak_NCR5380.h,v $
+ */
+
+#ifndef OAK_NCR5380_H
+#define OAK_NCR5380_H
+
+#define OAK_NCR5380_PUBLIC_RELEASE 1
+
+
+#ifndef ASM
+int oak_NCR5380_abort(Scsi_Cmnd *);
+int oak_NCR5380_detect(Scsi_Host_Template *);
+const char *oak_NCR5380_info(struct Scsi_Host *);
+int oak_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int oak_NCR5380_reset(Scsi_Cmnd *);
+
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#define OAK_NCR5380 {NULL, NULL, "Oak 16-bit SCSI",			\
+	oak_NCR5380_detect, NULL, oak_NCR5380_info, NULL,		\
+	oak_NCR5380_queue_command, oak_NCR5380_abort, 			\
+	oak_NCR5380_reset, NULL, 					\
+	NULL, /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL,		\
+	/* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#ifndef HOSTS_C
+#define NCR5380_implementation_fields \
+    int port, ctrl
+
+#define NCR5380_local_declare() \
+        struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+        _instance = instance
+
+#define NCR5380_read(reg) oak_NCR5380_read(_instance, reg)
+#define NCR5380_write(reg, value) oak_NCR5380_write(_instance, reg, value)
+
+#define NCR5380_intr oak_NCR5380_intr
+#define NCR5380_queue_command oak_NCR5380_queue_command
+#define NCR5380_abort oak_NCR5380_abort
+#define NCR5380_reset oak_NCR5380_reset
+
+#define BOARD_NORMAL	0
+#define BOARD_NCR53C400	1
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* OAK_NCR5380_H */
+
diff -urNwbB linux/arch/arm/drivers/scsi/scsi.c linux.arm/arch/arm/drivers/scsi/scsi.c
--- linux/arch/arm/drivers/scsi/scsi.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsi.c	Sat Feb 24 09:36:57 1996
@@ -0,0 +1,3148 @@
+/*
+ *  scsi.c Copyright (C) 1992 Drew Eckhardt
+ *         Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ *  generic mid-level SCSI driver
+ *      Initial versions: Drew Eckhardt
+ *      Subsequent revisions: Eric Youngdale
+ *
+ *  <drew@colorado.edu>
+ *
+ *  Bug correction thanks go to :
+ *      Rik Faith <faith@cs.unc.edu>
+ *      Tommy Thorn <tthorn>
+ *      Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
+ *
+ *  Modified by Eric Youngdale eric@aib.com to
+ *  add scatter-gather, multiple outstanding request, and other
+ *  enhancements.
+ *
+ *  Native multichannel and wide scsi support added 
+ *  by Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ */
+#ifdef MODULE
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include<linux/stat.h>
+
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+
+
+#undef USE_STATIC_SCSI_MEMORY
+
+/*
+static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.5 1993/09/24 12:45:18 drew Exp drew $";
+*/
+
+
+/* Command groups 3 and 4 are reserved and should never be used.  */
+const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
+
+#define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__))
+
+static void scsi_done (Scsi_Cmnd *SCpnt);
+static int update_timeout (Scsi_Cmnd *, int);
+static void print_inquiry(unsigned char *data);
+static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
+
+static unsigned char * dma_malloc_freelist = NULL;
+static int scsi_need_isa_bounce_buffers;
+static unsigned int dma_sectors = 0;
+unsigned int dma_free_sectors = 0;
+unsigned int need_isa_buffer = 0;
+static unsigned char ** dma_malloc_pages = NULL;
+
+static int time_start;
+static int time_elapsed;
+static volatile struct Scsi_Host * host_active = NULL;
+#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
+			  || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
+
+#define MAX_SCSI_DEVICE_CODE 10
+const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
+{
+    "Direct-Access    ",
+    "Sequential-Access",
+    "Printer          ",
+    "Processor        ",
+    "WORM             ",
+    "CD-ROM           ",
+    "Scanner          ",
+    "Optical Device   ",
+    "Medium Changer   ",
+    "Communications   "
+};
+
+
+/*
+ * global variables :
+ * scsi_devices an array of these specifying the address for each
+ * (host, id, LUN)
+ */
+
+Scsi_Device * scsi_devices = NULL;
+
+/* Process ID of SCSI commands */
+unsigned long scsi_pid = 0;
+
+static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
+static void resize_dma_pool(void);
+
+/* This variable is merely a hook so that we can debug the kernel with gdb. */
+Scsi_Cmnd * last_cmnd = NULL;
+
+/* This is the pointer to the /proc/scsi code. 
+ * It is only initialized to !=0 if the scsi code is present 
+ */ 
+extern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start, 
+				      off_t offset, int length, int inout); 
+extern int dispatch_scsi_info(int ino, char *buffer, char **start, 
+			      off_t offset, int length, int inout); 
+
+struct proc_dir_entry proc_scsi_scsi = {
+    PROC_SCSI_SCSI, 4, "scsi",
+    S_IFREG | S_IRUGO | S_IWUSR, 2, 0, 0, 0, 
+    NULL,
+    NULL, NULL,
+    NULL, NULL, NULL
+};
+
+
+/*
+ *  As the scsi do command functions are intelligent, and may need to
+ *  redo a command, we need to keep track of the last command
+ *  executed on each one.
+ */
+
+#define WAS_RESET       0x01
+#define WAS_TIMEDOUT    0x02
+#define WAS_SENSE       0x04
+#define IS_RESETTING    0x08
+#define IS_ABORTING     0x10
+#define ASKED_FOR_SENSE 0x20
+
+/*
+ *  This is the number  of clock ticks we should wait before we time out
+ *  and abort the command.  This is for  where the scsi.c module generates
+ *  the command, not where it originates from a higher level, in which
+ *  case the timeout is specified there.
+ *
+ *  ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT
+ *  respectively.
+ */
+
+#ifdef DEBUG_TIMEOUT
+static void scsi_dump_status(void);
+#endif
+
+
+#ifdef DEBUG
+    #define SCSI_TIMEOUT (5*HZ)
+#else
+    #define SCSI_TIMEOUT (1*HZ)
+#endif
+
+#ifdef DEBUG
+    #define SENSE_TIMEOUT SCSI_TIMEOUT
+    #define ABORT_TIMEOUT SCSI_TIMEOUT
+    #define RESET_TIMEOUT SCSI_TIMEOUT
+#else
+    #define SENSE_TIMEOUT (5*HZ/10)
+    #define RESET_TIMEOUT (5*HZ/10)
+    #define ABORT_TIMEOUT (5*HZ/10)
+#endif
+
+#define MIN_RESET_DELAY (1*HZ)
+
+/* Do not call reset on error if we just did a reset within 10 sec. */
+#define MIN_RESET_PERIOD (10*HZ)
+
+/* The following devices are known not to tolerate a lun != 0 scan for
+ * one reason or another.  Some will respond to all luns, others will
+ * lock up. 
+ */
+
+#define BLIST_NOLUN     0x01
+#define BLIST_FORCELUN  0x02
+#define BLIST_BORKEN    0x04
+#define BLIST_KEY       0x08
+#define BLIST_SINGLELUN 0x10
+
+struct dev_info{
+    const char * vendor;
+    const char * model;
+    const char * revision; /* Latest revision known to be bad.  Not used yet */
+    unsigned flags;
+};
+
+/*
+ * This is what was previously known as the blacklist.  The concept
+ * has been expanded so that we can specify other types of things we
+ * need to be aware of.
+ */
+static struct dev_info device_list[] =
+{
+{"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"DENON","DRD-25X","V", BLIST_NOLUN},           /* Locks up if probed for lun != 0 */
+{"HITACHI","DK312C","CM81", BLIST_NOLUN},       /* Responds to all lun - dtg */
+{"HITACHI","DK314C","CR21" , BLIST_NOLUN},      /* responds to all lun */
+{"IMS", "CDD521/10","2.06", BLIST_NOLUN},       /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-3280","PR02", BLIST_NOLUN},       /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-4380S","B3C", BLIST_NOLUN},       /* Locks-up when LUN>0 polled. */
+{"MAXTOR","MXT-1240S","I1.2", BLIST_NOLUN},     /* Locks up when LUN>0 polled */
+{"MAXTOR","XT-4170S","B5A", BLIST_NOLUN},       /* Locks-up sometimes when LUN>0 polled. */
+{"MAXTOR","XT-8760S","B7B", BLIST_NOLUN},       /* guess what? */
+{"NEC","CD-ROM DRIVE:841","1.0", BLIST_NOLUN},  /* Locks-up when LUN>0 polled. */
+{"RODIME","RO3000S","2.33", BLIST_NOLUN},       /* Locks up if polled for lun != 0 */
+{"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN},   /* causes failed REQUEST SENSE on lun 1 
+						 * for aha152x controller, which causes 
+						 * SCSI code to reset bus.*/
+{"SEAGATE", "ST296","921", BLIST_NOLUN},        /* Responds to all lun */
+{"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN},
+{"TANDBERG","TDC 3600","U07", BLIST_NOLUN},     /* Locks up if polled for lun != 0 */
+{"TEAC","CD-ROM","1.06", BLIST_NOLUN},          /* causes failed REQUEST SENSE on lun 1 
+						 * for seagate controller, which causes 
+						 * SCSI code to reset bus.*/
+{"TEXEL","CD-ROM","1.06", BLIST_NOLUN},         /* causes failed REQUEST SENSE on lun 1 
+						 * for seagate controller, which causes 
+						 * SCSI code to reset bus.*/
+{"QUANTUM","LPS525S","3110", BLIST_NOLUN},      /* Locks sometimes if polled for lun != 0 */
+{"QUANTUM","PD1225S","3110", BLIST_NOLUN},      /* Locks sometimes if polled for lun != 0 */
+{"MEDIAVIS","CDR-H93MV","1.31", BLIST_NOLUN},   /* Locks up if polled for lun != 0 */
+{"SANKYO", "CP525","6.64", BLIST_NOLUN},        /* causes failed REQ SENSE, extra reset */
+{"HP", "C1750A", "3226", BLIST_NOLUN},          /* scanjet iic */
+{"HP", "C1790A", "", BLIST_NOLUN},              /* scanjet iip */
+{"HP", "C2500A", "", BLIST_NOLUN},              /* scanjet iicx */
+
+/*
+ * Other types of devices that have special flags.
+ */
+{"SONY","CD-ROM CDU-8001","*", BLIST_BORKEN},
+{"TEXEL","CD-ROM","1.06", BLIST_BORKEN},
+{"INSITE","Floptical   F*8I","*", BLIST_KEY},
+{"INSITE","I325VM","*", BLIST_KEY},
+{"PIONEER","CD-ROMDRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROMDRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+/*
+ * Must be at end of list...
+ */
+{NULL, NULL, NULL}
+};
+
+static int get_device_flags(unsigned char * response_data){
+    int i = 0;
+    unsigned char * pnt;
+    for(i=0; 1; i++){
+	if(device_list[i].vendor == NULL) return 0;
+	pnt = &response_data[8];
+	while(*pnt && *pnt == ' ') pnt++;
+	if(memcmp(device_list[i].vendor, pnt,
+		  strlen(device_list[i].vendor))) continue;
+	pnt = &response_data[16];
+	while(*pnt && *pnt == ' ') pnt++;
+	if(memcmp(device_list[i].model, pnt,
+		  strlen(device_list[i].model))) continue;
+	return device_list[i].flags;
+    }
+    return 0;
+}
+
+/*
+ *  As the actual SCSI command runs in the background, we must set up a
+ *  flag that tells scan_scsis() when the result it has is valid.
+ *  scan_scsis can set the_result to -1, and watch for it to become the
+ *  actual return code for that call.  the scan_scsis_done function() is
+ *  our user specified completion function that is passed on to the
+ *  scsi_do_cmd() function.
+ */
+
+volatile int in_scan_scsis = 0;
+static int the_result;
+
+void scsi_make_blocked_list(void)  {
+    int block_count = 0, index;
+    unsigned int flags;
+    struct Scsi_Host * sh[128], * shpnt;
+    
+    /*
+     * Create a circular linked list from the scsi hosts which have
+     * the "wish_block" field in the Scsi_Host structure set.
+     * The blocked list should include all the scsi hosts using ISA DMA.
+     * In some systems, using two dma channels simultaneously causes
+     * unpredictable results.
+     * Among the scsi hosts in the blocked list, only one host at a time
+     * is allowed to have active commands queued. The transition from
+     * one active host to the next one is allowed only when host_busy == 0
+     * for the active host (which implies host_busy == 0 for all the hosts
+     * in the list). Moreover for block devices the transition to a new
+     * active host is allowed only when a request is completed, since a
+     * block device request can be divided into multiple scsi commands
+     * (when there are few sg lists or clustering is disabled).
+     *
+     * (DB, 4 Feb 1995)
+     */
+    
+    save_flags(flags);
+    cli();
+    host_active = NULL;
+    
+    for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {
+	
+#if 0
+	/*
+	 * Is this is a candidate for the blocked list?
+	 * Useful to put into the blocked list all the hosts whose driver
+	 * does not know about the host->block feature.
+	 */
+	if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;
+#endif
+	
+	if (shpnt->wish_block) sh[block_count++] = shpnt;
+    }
+    
+    if (block_count == 1) sh[0]->block = NULL;
+    
+    else if (block_count > 1) {
+	
+	for(index = 0; index < block_count - 1; index++) {
+	    sh[index]->block = sh[index + 1];
+	    printk("scsi%d : added to blocked host list.\n",
+		   sh[index]->host_no);
+	}
+	
+	sh[block_count - 1]->block = sh[0];
+	printk("scsi%d : added to blocked host list.\n",
+	       sh[index]->host_no);
+    }
+    
+    restore_flags(flags);
+}
+
+static void scan_scsis_done (Scsi_Cmnd * SCpnt)
+{
+    
+#ifdef DEBUG
+    printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result);
+#endif
+    SCpnt->request.rq_status = RQ_SCSI_DONE;
+    
+    if (SCpnt->request.sem != NULL)
+	up(SCpnt->request.sem);
+}
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+static int max_scsi_luns = 8;
+#else
+static int max_scsi_luns = 1;
+#endif
+
+void scsi_luns_setup(char *str, int *ints) {
+    if (ints[0] != 1)
+	printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n");
+    else
+	max_scsi_luns = ints[1];
+}
+
+/*
+ *  Detecting SCSI devices :
+ *  We scan all present host adapter's busses,  from ID 0 to ID (max_id).
+ *  We use the INQUIRY command, determine device type, and pass the ID /
+ *  lun address of all sequential devices to the tape driver, all random
+ *  devices to the disk driver.
+ */
+static
+void scan_scsis (struct Scsi_Host * shpnt, unchar hardcoded, 
+		 unchar hchannel, unchar hid, unchar hlun)
+{
+    int dev, lun, type, channel;
+    unsigned char scsi_cmd [12];
+    unsigned char scsi_result0 [256];
+    unsigned char * scsi_result;
+    Scsi_Device * SDpnt, *SDtail;
+    struct Scsi_Device_Template * sdtpnt;
+    int                 bflags;
+    int                 max_dev_lun = 0;
+    Scsi_Cmnd  *SCpnt;
+    
+    ++in_scan_scsis;
+    lun = 0;
+    type = -1;
+    SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC|GFP_DMA);
+    SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
+    SDtail = scsi_devices;
+    
+    if(scsi_devices) while(SDtail->next) SDtail = SDtail->next;
+    
+    /* Make sure we have something that is valid for DMA purposes */
+    scsi_result = ((!dma_malloc_freelist  || !shpnt->unchecked_isa_dma)
+		   ?  &scsi_result0[0] : scsi_malloc(512));
+    
+    if(scsi_result == NULL) {
+	printk("Unable to obtain scsi_result buffer\n");
+	goto leave;
+    }
+    
+    shpnt->host_queue = SCpnt; /* We need this so that commands can time out */
+
+    if(hardcoded == 1) {
+	channel = hchannel;
+	dev = hid;
+	lun = hlun;
+	goto crude; /* Anyone remember good ol' BASIC ?  :-) */
+    }
+
+    for (channel = 0; channel <= shpnt->max_channel; channel++)
+    {
+	for (dev = 0; dev < shpnt->max_id; ++dev) {
+	    if (shpnt->this_id != dev) {
+		
+		/*
+		 * We need the for so our continue, etc. work fine.
+		 * We put this in a variable so that we can override
+		 * it during the scan if we detect a device *KNOWN*
+		 * to have multiple logical units.
+		 */
+		max_dev_lun = (max_scsi_luns < shpnt->max_lun ? 
+			       max_scsi_luns : shpnt->max_lun);
+
+		for (lun = 0; lun < max_dev_lun; ++lun)
+		{
+		crude:
+		    memset(SDpnt, 0, sizeof(Scsi_Device));
+		    SDpnt->host = shpnt;
+		    SDpnt->id = dev;
+		    SDpnt->lun = lun;
+		    SDpnt->channel = channel;
+
+		    /* Some low level driver could use device->type (DB) */
+		    SDpnt->type = -1;
+		    /*
+		     * Assume that the device will have handshaking problems, 
+		     * and then fix this field later if it turns out it doesn't
+		     */
+		    SDpnt->borken = 1;
+                    SDpnt->was_reset = 0;
+                    SDpnt->expecting_cc_ua = 0;
+		    
+		    scsi_cmd[0] = TEST_UNIT_READY;
+		    scsi_cmd[1] = lun << 5;
+		    scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+		    
+		    memset(SCpnt, 0,  sizeof(Scsi_Cmnd));
+		    SCpnt->host = SDpnt->host;
+		    SCpnt->device = SDpnt;
+		    SCpnt->target = SDpnt->id;
+		    SCpnt->lun = SDpnt->lun;
+		    SCpnt->channel = SDpnt->channel;
+
+		    {
+			/*
+			 * Do the actual command, and wait for it to finish
+			 */
+			struct semaphore sem = MUTEX_LOCKED;
+			SCpnt->request.sem = &sem;
+			SCpnt->request.rq_status = RQ_SCSI_BUSY;
+			scsi_do_cmd (SCpnt, (void *)  scsi_cmd, 
+				     (void *) scsi_result,
+				     256,  scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+			down(&sem);
+		    }
+
+#if defined(DEBUG) || defined(DEBUG_INIT)
+		    printk("scsi: scan SCSIS id %d lun %d\n", dev, lun);
+		    printk("scsi: return code %08x\n", SCpnt->result);
+#endif
+
+		    if(SCpnt->result) {
+			if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
+			     (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
+			    ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+			    if (SCpnt->sense_buffer[2] &0xe0)
+				continue; /* No devices here... */
+			    if(((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+			       ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION))
+				continue;
+			}
+			else
+			    break;
+		    }
+		    
+#if defined (DEBUG) || defined(DEBUG_INIT)
+		    printk("scsi: performing INQUIRY\n");
+#endif
+
+		    /*
+		     * Build an INQUIRY command block.
+		     */
+		    scsi_cmd[0] = INQUIRY;
+		    scsi_cmd[1] = (lun << 5) & 0xe0;
+		    scsi_cmd[2] = 0;
+		    scsi_cmd[3] = 0;
+		    scsi_cmd[4] = 255;
+		    scsi_cmd[5] = 0;
+		    
+		    SCpnt->cmd_len = 0;
+
+		    {
+			struct semaphore sem = MUTEX_LOCKED;
+			SCpnt->request.sem = &sem;
+			SCpnt->request.rq_status = RQ_SCSI_BUSY;
+			scsi_do_cmd (SCpnt, (void *)  scsi_cmd, 
+				 (void *) scsi_result,
+				 256,  scan_scsis_done, SCSI_TIMEOUT, 3);
+			down(&sem);
+		    }
+		    
+		    the_result = SCpnt->result;
+		    
+#if defined(DEBUG) || defined(DEBUG_INIT)
+		    if (!the_result)
+			printk("scsi: INQUIRY successful\n");
+		    else
+			printk("scsi: INQUIRY failed with code %08x\n", the_result);
+#endif
+		    
+		    if(the_result) break;
+		    
+		    /* skip other luns on this device */
+		    
+		    if (!the_result)
+		    {
+			/* It would seem some TOSHIBA CDROM 
+			 * gets things wrong 
+			 */
+			if (!strncmp(scsi_result+8,"TOSHIBA",7) &&
+			    !strncmp(scsi_result+16,"CD-ROM",6) &&
+			    scsi_result[0] == TYPE_DISK) {
+			    scsi_result[0] = TYPE_ROM;
+			    scsi_result[1] |= 0x80;  /* removable */
+			}
+			
+			if (!strncmp(scsi_result+8,"NEC",3)) {
+			    if (!strncmp(scsi_result+16,"CD-ROM DRIVE:84 ",16) || 
+				!strncmp(scsi_result+16,"CD-ROM DRIVE:25",15))
+				SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
+			    else
+				SDpnt->manufacturer = SCSI_MAN_NEC;
+			} else if (!strncmp(scsi_result+8,"TOSHIBA",7))
+			    SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+			else if (!strncmp(scsi_result+8,"SONY",4))
+			    SDpnt->manufacturer = SCSI_MAN_SONY;
+                        else if (!strncmp(scsi_result+8, "PIONEER", 7))
+                            SDpnt->manufacturer = SCSI_MAN_PIONEER;
+			else
+			    SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
+			
+			memcpy(SDpnt->vendor, scsi_result+8, 8);
+			memcpy(SDpnt->model, scsi_result+16, 16);
+			memcpy(SDpnt->rev, scsi_result+32, 4);
+ 
+			SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+			SDpnt->lockable = SDpnt->removable;
+			SDpnt->changed = 0;
+			SDpnt->access_count = 0;
+			SDpnt->busy = 0;
+			SDpnt->has_cmdblocks = 0;
+			/*
+			 * Currently, all sequential devices are assumed to be
+			 * tapes, all random devices disk, with the appropriate
+			 * read only flags set for ROM / WORM treated as RO.
+			 */
+			
+			switch (type = (scsi_result[0] & 0x1f))
+			{
+			case TYPE_TAPE :
+			case TYPE_DISK :
+			case TYPE_MOD :
+			case TYPE_PROCESSOR :
+			case TYPE_SCANNER :
+			    SDpnt->writeable = 1;
+			    break;
+			case TYPE_WORM :
+			case TYPE_ROM :
+			    SDpnt->writeable = 0;
+			    break;
+			default :
+#if 0
+#ifdef DEBUG
+			    printk("scsi: unknown type %d\n", type);
+			    print_inquiry(scsi_result);
+#endif
+			    type = -1;
+#endif
+			}
+			
+			SDpnt->single_lun = 0;
+			SDpnt->soft_reset =
+			    (scsi_result[7] & 1) && ((scsi_result[3] &7) == 2);
+			SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
+			SDpnt->type = (type & 0x1f);
+			
+			if (type != -1)
+			{
+			    print_inquiry(scsi_result);
+			    
+			    for(sdtpnt = scsi_devicelist; sdtpnt; 
+				sdtpnt = sdtpnt->next)
+				if(sdtpnt->detect) SDpnt->attached +=
+				    (*sdtpnt->detect)(SDpnt);
+			    
+			    SDpnt->scsi_level = scsi_result[2] & 0x07;
+			    if (SDpnt->scsi_level >= 2 ||
+				(SDpnt->scsi_level == 1 &&
+				 (scsi_result[3] & 0x0f) == 1))
+				SDpnt->scsi_level++;
+			    /*
+			     * Set the tagged_queue flag for SCSI-II devices 
+			     * that purport to support
+			     * tagged queuing in the INQUIRY data.
+			     */
+			    
+			    SDpnt->tagged_queue = 0;
+			    
+			    if ((SDpnt->scsi_level >= SCSI_2) &&
+				(scsi_result[7] & 2)) {
+				SDpnt->tagged_supported = 1;
+				SDpnt->current_tag = 0;
+			    }
+			    
+			    /*
+			     * Accommodate drivers that want to sleep when 
+			     * they should be in a polling loop.
+			     */
+
+			    SDpnt->disconnect = 0;
+
+			    /*
+			     * Get any flags for this device.
+			     */
+			    bflags = get_device_flags(scsi_result);
+
+			    
+			    /*
+			     * Some revisions of the Texel CD ROM drives have 
+			     * handshaking problems when used with the Seagate
+			     * controllers.  Before we know what type of device
+			     * we're talking to, we assume it's borken and then
+			     * change it here if it turns out that it isn't
+			     * a TEXEL drive.
+			     */
+			    if( (bflags & BLIST_BORKEN) == 0 )
+			    {
+				SDpnt->borken = 0;
+			    }
+			    
+			    
+			    /* These devices need this "key" to unlock the
+			     * devices so we can use it 
+			     */
+			    if( (bflags & BLIST_KEY) != 0 ) {
+				printk("Unlocked floptical drive.\n");
+				SDpnt->lockable = 0;
+				scsi_cmd[0] = MODE_SENSE;
+				scsi_cmd[1] = (lun << 5) & 0xe0;
+				scsi_cmd[2] = 0x2e;
+				scsi_cmd[3] = 0;
+				scsi_cmd[4] = 0x2a;
+				scsi_cmd[5] = 0;
+				
+				SCpnt->cmd_len = 0;
+				{
+				    struct semaphore sem = MUTEX_LOCKED;
+				    SCpnt->request.rq_status = RQ_SCSI_BUSY;
+				    SCpnt->request.sem = &sem;
+				    scsi_do_cmd (SCpnt, (void *)  scsi_cmd,
+					     (void *) scsi_result, 0x2a,  
+					     scan_scsis_done, SCSI_TIMEOUT, 3);
+				    down(&sem);
+				}
+			    }
+			    /* Add this device to the linked list at the end */
+			    if(SDtail)
+				SDtail->next = SDpnt;
+			    else
+				scsi_devices = SDpnt;
+			    SDtail = SDpnt;
+			    
+			    SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
+			    /* Some scsi devices cannot be polled for lun != 0
+			     * due to firmware bugs 
+			     */
+			    if(bflags & BLIST_NOLUN) break;
+
+			    /*
+			     * If we want to only allow I/O to one of the luns
+			     * attached to this device at a time, then we set 
+                             * this flag.
+			     */
+			    if(bflags & BLIST_SINGLELUN)
+			    {
+				SDpnt->single_lun = 1;
+			    }
+
+			    /*
+			     * If this device is known to support multiple 
+                             * units, override the other settings, and scan 
+                             * all of them.
+			     */
+			    if(bflags & BLIST_FORCELUN)
+			    {
+				/*
+				 * We probably want to make this a variable, 
+                                 * but this will do for now.
+				 */
+				max_dev_lun = 8;
+			    }
+ 
+
+			    /* Old drives like the MAXTOR XT-3280 say vers=0 */
+			    if ((scsi_result[2] & 0x07) == 0)
+				break;
+			    /* Some scsi-1 peripherals do not handle lun != 0.
+			     * I am assuming that scsi-2 peripherals do better 
+			     */
+			    if((scsi_result[2] & 0x07) == 1 &&
+			       (scsi_result[3] & 0x0f) == 0) break;
+			}
+		    }       /* if result == DID_OK ends */
+
+		    /*
+		     * This might screw us up with multi-lun devices, but the 
+                     * user can scan for them too.
+		     */
+		    if(hardcoded == 1)
+			goto leave;
+		} /* for lun ends */
+	    } /* if this_id != id ends */
+	} /* for dev ends */
+    } /* for channel ends */
+    
+ leave:
+    shpnt->host_queue = NULL;  /* No longer needed here */
+    
+    /* Last device block does not exist.  Free memory. */
+    if(SDpnt != NULL)
+	scsi_init_free((char *) SDpnt, sizeof(Scsi_Device));
+    
+    if(SCpnt != NULL)
+	scsi_init_free((char *) SCpnt, sizeof(Scsi_Cmnd));
+    
+    /* If we allocated a buffer so we could do DMA, free it now */
+    if (scsi_result != &scsi_result0[0] && scsi_result != NULL) 
+	scsi_free(scsi_result, 512);
+    
+    in_scan_scsis = 0;
+}       /* scan_scsis  ends */
+
+/*
+ *  Flag bits for the internal_timeout array
+ */
+
+#define NORMAL_TIMEOUT 0
+#define IN_ABORT 1
+#define IN_RESET 2
+
+/*
+ * This is our time out function, called when the timer expires for a
+ * given host adapter.  It will attempt to abort the currently executing
+ * command, that failing perform a kernel panic.
+ */
+
+static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid)
+{
+    
+    switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET))
+    {
+    case NORMAL_TIMEOUT:
+	if (!in_scan_scsis) {
+#ifdef DEBUG_TIMEOUT
+	    scsi_dump_status();
+#endif
+	}
+	
+	if (!scsi_abort (SCpnt, DID_TIME_OUT, pid))
+	    return;
+    case IN_ABORT:
+	printk("SCSI host %d abort() timed out - resetting\n",
+	       SCpnt->host->host_no);
+	if (!scsi_reset (SCpnt, FALSE))
+	    return;
+    case IN_RESET:
+    case (IN_ABORT | IN_RESET):
+	/* This might be controversial, but if there is a bus hang,
+	 * you might conceivably want the machine up and running
+	 * esp if you have an ide disk. 
+	 */
+	printk("Unable to reset scsi host %d - ", SCpnt->host->host_no);
+	printk("probably a SCSI bus hang.\n");
+        scsi_reset (SCpnt, TRUE);
+	return;
+	
+    default:
+	INTERNAL_ERROR;
+    }
+    
+}
+
+
+/* This function takes a quick look at a request, and decides if it
+ * can be queued now, or if there would be a stall while waiting for
+ * something else to finish.  This routine assumes that interrupts are
+ * turned off when entering the routine.  It is the responsibility
+ * of the calling code to ensure that this is the case. 
+ */
+
+Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
+{
+    Scsi_Cmnd * SCpnt = NULL;
+    int tablesize;
+    Scsi_Cmnd * found = NULL;
+    struct buffer_head * bh, *bhp;
+    
+    if (!device)
+	panic ("No device passed to request_queueable().\n");
+    
+    if (req && req->rq_status == RQ_INACTIVE)
+	panic("Inactive in request_queueable");
+    
+    SCpnt =  device->host->host_queue;
+
+    /*
+     * Look for a free command block.  If we have been instructed not to queue
+     * multiple commands to multi-lun devices, then check to see what else is 
+     * going for this device first.
+     */
+      
+    SCpnt = device->host->host_queue;
+    if (!device->single_lun) {
+	while(SCpnt){
+	    if(SCpnt->target == device->id &&
+	       SCpnt->lun == device->lun) {
+		if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+	    }
+	    SCpnt = SCpnt->next;
+	}
+    } else {
+	while(SCpnt){
+	    if(SCpnt->target == device->id) {
+		if (SCpnt->lun == device->lun) {
+		    if(found == NULL 
+		       && SCpnt->request.rq_status == RQ_INACTIVE) 
+		    {
+			found=SCpnt;
+		    }
+		} 
+		if(SCpnt->request.rq_status != RQ_INACTIVE) {
+		    /*
+		     * I think that we should really limit things to one
+		     * outstanding command per device - this is what tends 
+                     * to trip up buggy firmware.
+		     */
+		    return NULL;
+		}
+	    }
+	    SCpnt = SCpnt->next;
+	}
+	SCpnt = found;
+    }
+    
+    if (!SCpnt) return NULL;
+    
+    if (SCSI_BLOCK(device->host)) return NULL;
+    
+    if (req) {
+	memcpy(&SCpnt->request, req, sizeof(struct request));
+	tablesize = device->host->sg_tablesize;
+	bhp = bh = req->bh;
+	if(!tablesize) bh = NULL;
+	/* Take a quick look through the table to see how big it is.  
+	 * We already have our copy of req, so we can mess with that 
+	 * if we want to. 
+	 */
+	while(req->nr_sectors && bh){
+	    bhp = bhp->b_reqnext;
+	    if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+	    req->nr_sectors -= bh->b_size >> 9;
+	    req->sector += bh->b_size >> 9;
+	    if(!tablesize) break;
+	    bh = bhp;
+	}
+	if(req->nr_sectors && bh && bh->b_reqnext){  /* Any leftovers? */
+	    SCpnt->request.bhtail = bh;
+	    req->bh = bh->b_reqnext; /* Divide request */
+	    bh->b_reqnext = NULL;
+	    bh = req->bh;
+	    
+	    /* Now reset things so that req looks OK */
+	    SCpnt->request.nr_sectors -= req->nr_sectors;
+	    req->current_nr_sectors = bh->b_size >> 9;
+	    req->buffer = bh->b_data;
+	    SCpnt->request.sem = NULL; /* Wait until whole thing done */
+	} else {
+	    req->rq_status = RQ_INACTIVE;
+	    wake_up(&wait_for_request);
+	}
+    } else {
+	SCpnt->request.rq_status = RQ_SCSI_BUSY;  /* Busy, but no request */
+	SCpnt->request.sem = NULL;   /* And no one is waiting for the device 
+				      * either */
+    }
+    
+    SCpnt->use_sg = 0;               /* Reset the scatter-gather flag */
+    SCpnt->old_use_sg  = 0;
+    SCpnt->transfersize = 0;
+    SCpnt->underflow = 0;
+    SCpnt->cmd_len = 0;
+
+/* Since not everyone seems to set the device info correctly
+ * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ */ 
+    SCpnt->channel = device->channel;
+    SCpnt->lun = device->lun;
+    SCpnt->target = device->id;
+
+    return SCpnt;
+}
+
+/* This function returns a structure pointer that will be valid for
+ * the device.  The wait parameter tells us whether we should wait for
+ * the unit to become free or not.  We are also able to tell this routine
+ * not to return a descriptor if the host is unable to accept any more
+ * commands for the time being.  We need to keep in mind that there is no
+ * guarantee that the host remain not busy.  Keep in mind the
+ * request_queueable function also knows the internal allocation scheme
+ * of the packets for each device 
+ */
+
+Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
+			     int wait)
+{
+    kdev_t dev;
+    struct request * req = NULL;
+    int tablesize;
+    unsigned int flags;
+    struct buffer_head * bh, *bhp;
+    struct Scsi_Host * host;
+    Scsi_Cmnd * SCpnt = NULL;
+    Scsi_Cmnd * SCwait = NULL;
+    Scsi_Cmnd * found = NULL;
+    
+    if (!device)
+	panic ("No device passed to allocate_device().\n");
+    
+    if (reqp) req = *reqp;
+    
+    /* See if this request has already been queued by an interrupt routine */
+    if (req) {
+	if(req->rq_status == RQ_INACTIVE) return NULL;
+	dev = req->rq_dev;
+    } else
+        dev = 0;		/* unused */
+    
+    host = device->host;
+    
+    if (intr_count && SCSI_BLOCK(host)) return NULL;
+    
+    while (1==1){
+	SCpnt = device->host->host_queue;
+	if (!device->single_lun) {
+	    while(SCpnt){
+		if(SCpnt->target == device->id &&
+		   SCpnt->lun == device->lun) {
+		   SCwait = SCpnt;
+		    if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+		}
+		SCpnt = SCpnt->next;
+	    }
+	} else {
+	    while(SCpnt){
+		if(SCpnt->target == device->id) {
+		    if (SCpnt->lun == device->lun) {
+			SCwait = SCpnt;
+			if(found == NULL 
+			   && SCpnt->request.rq_status == RQ_INACTIVE) 
+			{
+			    found=SCpnt;
+			}
+		    } 
+		    if(SCpnt->request.rq_status != RQ_INACTIVE) {
+			/*
+			 * I think that we should really limit things to one
+			 * outstanding command per device - this is what tends
+                         * to trip up buggy firmware.
+			 */
+			found = NULL;
+			break;
+		    }
+		}
+		SCpnt = SCpnt->next;
+	    }
+	    SCpnt = found;
+	}
+
+	save_flags(flags);
+	cli();
+	/* See if this request has already been queued by an interrupt routine
+	 */
+	if (req && (req->rq_status == RQ_INACTIVE || req->rq_dev != dev)) {
+	    restore_flags(flags);
+	    return NULL;
+	}
+	if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE)  /* Might have changed */
+	{
+	    restore_flags(flags);
+	    if(!wait) return NULL;
+	    if (!SCwait) {
+		printk("Attempt to allocate device channel %d, target %d, "
+		       "lun %d\n", device->channel, device->id, device->lun);
+		panic("No device found in allocate_device\n");
+	    }
+	    SCSI_SLEEP(&device->device_wait,
+		       (SCwait->request.rq_status != RQ_INACTIVE));
+	} else {
+	    if (req) {
+		memcpy(&SCpnt->request, req, sizeof(struct request));
+		tablesize = device->host->sg_tablesize;
+		bhp = bh = req->bh;
+		if(!tablesize) bh = NULL;
+		/* Take a quick look through the table to see how big it is.  
+		 * We already have our copy of req, so we can mess with that 
+		 * if we want to.  
+		 */
+		while(req->nr_sectors && bh){
+		    bhp = bhp->b_reqnext;
+		    if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+		    req->nr_sectors -= bh->b_size >> 9;
+		    req->sector += bh->b_size >> 9;
+		    if(!tablesize) break;
+		    bh = bhp;
+		}
+		if(req->nr_sectors && bh && bh->b_reqnext){/* Any leftovers? */
+		    SCpnt->request.bhtail = bh;
+		    req->bh = bh->b_reqnext; /* Divide request */
+		    bh->b_reqnext = NULL;
+		    bh = req->bh;
+		    /* Now reset things so that req looks OK */
+		    SCpnt->request.nr_sectors -= req->nr_sectors;
+		    req->current_nr_sectors = bh->b_size >> 9;
+		    req->buffer = bh->b_data;
+		    SCpnt->request.sem = NULL; /* Wait until whole thing done*/
+		}
+		else
+		{
+		    req->rq_status = RQ_INACTIVE;
+		    *reqp = req->next;
+		    wake_up(&wait_for_request);
+		}
+	    } else {
+		SCpnt->request.rq_status = RQ_SCSI_BUSY;
+		SCpnt->request.sem = NULL;   /* And no one is waiting for this 
+					      * to complete */
+	    }
+	    restore_flags(flags);
+	    break;
+	}
+    }
+    
+    SCpnt->use_sg = 0;            /* Reset the scatter-gather flag */
+    SCpnt->old_use_sg  = 0;
+    SCpnt->transfersize = 0;      /* No default transfer size */
+    SCpnt->cmd_len = 0;
+
+    SCpnt->underflow = 0;         /* Do not flag underflow conditions */
+
+    /* Since not everyone seems to set the device info correctly
+     * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+     */ 
+    SCpnt->channel = device->channel;
+    SCpnt->lun = device->lun;
+    SCpnt->target = device->id;
+
+    return SCpnt;
+}
+
+/*
+ * This is inline because we have stack problemes if we recurse to deeply.
+ */
+
+inline void internal_cmnd (Scsi_Cmnd * SCpnt)
+{
+    int temp;
+    struct Scsi_Host * host;
+    unsigned int flags;
+#ifdef DEBUG_DELAY
+    int clock;
+#endif
+    
+    host = SCpnt->host;
+    
+    /*
+     * We will wait MIN_RESET_DELAY clock ticks after the last reset so
+     * we can avoid the drive not being ready.
+     */
+    save_flags(flags);
+    sti();
+    temp = host->last_reset + MIN_RESET_DELAY;
+    while (jiffies < temp);
+    restore_flags(flags);
+    
+    update_timeout(SCpnt, SCpnt->timeout_per_command);
+    
+    /*
+     * We will use a queued command if possible, otherwise we will emulate the
+     * queuing and calling of completion function ourselves.
+     */
+#ifdef DEBUG
+    printk("internal_cmnd (host = %d, channel = %d, target = %d, "
+	   "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n", 
+	   SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd, 
+	   SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
+#endif
+    
+    if (host->can_queue)
+    {
+#ifdef DEBUG
+	printk("queuecommand : routine at %p\n",
+	       host->hostt->queuecommand);
+#endif
+	/* This locking tries to prevent all sorts of races between
+	 * queuecommand and the interrupt code.  In effect,
+	 * we are only allowed to be in queuecommand once at
+	 * any given time, and we can only be in the interrupt
+	 * handler and the queuecommand function at the same time
+	 * when queuecommand is called while servicing the
+	 * interrupt. 
+	 */
+	
+	if(!intr_count && SCpnt->host->irq)
+	    disable_irq(SCpnt->host->irq);
+	
+	host->hostt->queuecommand (SCpnt, scsi_done);
+	
+	if(!intr_count && SCpnt->host->irq)
+	    enable_irq(SCpnt->host->irq);
+    }
+    else
+    {
+	
+#ifdef DEBUG
+	printk("command() :  routine at %p\n", host->hostt->command);
+#endif
+	temp=host->hostt->command (SCpnt);
+	SCpnt->result = temp;
+#ifdef DEBUG_DELAY
+	clock = jiffies + 4 * HZ;
+	while (jiffies < clock);
+	printk("done(host = %d, result = %04x) : routine at %08x\n", 
+	       host->host_no, temp);
+#endif
+	scsi_done(SCpnt);
+    }
+#ifdef DEBUG
+    printk("leaving internal_cmnd()\n");
+#endif
+}
+
+static void scsi_request_sense (Scsi_Cmnd * SCpnt)
+{
+    unsigned int flags;
+    
+    save_flags(flags);
+    cli();
+    SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;
+    update_timeout(SCpnt, SENSE_TIMEOUT);
+    restore_flags(flags);
+    
+    
+    memcpy ((void *) SCpnt->cmnd , (void *) generic_sense, 
+	    sizeof(generic_sense));
+    
+    SCpnt->cmnd[1] = SCpnt->lun << 5;
+    SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
+    
+    SCpnt->request_buffer = &SCpnt->sense_buffer;
+    SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
+    SCpnt->use_sg = 0;
+    SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+    internal_cmnd (SCpnt);
+}
+
+
+
+/*
+ * scsi_do_cmd sends all the commands out to the low-level driver.  It
+ * handles the specifics required for each low level driver - ie queued
+ * or non queued.  It also prevents conflicts when different high level
+ * drivers go for the same host at the same time.
+ */
+
+void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
+		  void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),
+		  int timeout, int retries)
+{
+    unsigned long flags;
+    struct Scsi_Host * host = SCpnt->host;
+    
+#ifdef DEBUG
+    {
+	int i;
+	int target = SCpnt->target;
+	printk ("scsi_do_cmd (host = %d, channel = %d target = %d, "
+		"buffer =%p, bufflen = %d, done = %p, timeout = %d, "
+		"retries = %d)\n"
+		"command : " , host->host_no, SCpnt->channel, target, buffer, 
+		bufflen, done, timeout, retries);
+	for (i = 0; i < 10; ++i)
+	    printk ("%02x  ", ((unsigned char *) cmnd)[i]);
+	printk("\n");
+    }
+#endif
+    
+    if (!host)
+    {
+	panic ("Invalid or not present host.\n");
+    }
+    
+    
+    /*
+     * We must prevent reentrancy to the lowlevel host driver.  This prevents
+     * it - we enter a loop until the host we want to talk to is not busy.
+     * Race conditions are prevented, as interrupts are disabled in between the
+     * time we check for the host being not busy, and the time we mark it busy
+     * ourselves.
+     */
+
+    save_flags(flags);
+    cli();
+    SCpnt->pid = scsi_pid++;
+    
+    while (SCSI_BLOCK(host)) {
+	restore_flags(flags);
+	SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host));
+	cli();
+    }
+    
+    if (host->block) host_active = host;
+    
+    host->host_busy++;
+    restore_flags(flags);
+    
+    /*
+     * Our own function scsi_done (which marks the host as not busy, disables
+     * the timeout counter, etc) will be called by us or by the
+     * scsi_hosts[host].queuecommand() function needs to also call
+     * the completion function for the high level driver.
+     */
+    
+    memcpy ((void *) SCpnt->data_cmnd , (const void *) cmnd, 12);
+#if 0
+    SCpnt->host = host;
+    SCpnt->channel = channel;
+    SCpnt->target = target;
+    SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);
+#endif
+    SCpnt->bufflen = bufflen;
+    SCpnt->buffer = buffer;
+    SCpnt->flags=0;
+    SCpnt->retries=0;
+    SCpnt->allowed=retries;
+    SCpnt->done = done;
+    SCpnt->timeout_per_command = timeout;
+
+    memcpy ((void *) SCpnt->cmnd , (const void *) cmnd, 12);
+    /* Zero the sense buffer.  Some host adapters automatically request
+     * sense on error.  0 is not a valid sense code.  
+     */
+    memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
+    SCpnt->request_buffer = buffer;
+    SCpnt->request_bufflen = bufflen;
+    SCpnt->old_use_sg = SCpnt->use_sg;
+    if (SCpnt->cmd_len == 0)
+	SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+    SCpnt->old_cmd_len = SCpnt->cmd_len;
+
+    /* Start the timer ticking.  */
+
+    SCpnt->internal_timeout = 0;
+    SCpnt->abort_reason = 0;
+    internal_cmnd (SCpnt);
+
+#ifdef DEBUG
+    printk ("Leaving scsi_do_cmd()\n");
+#endif
+}
+
+static int check_sense (Scsi_Cmnd * SCpnt)
+{
+    /* If there is no sense information, request it.  If we have already
+     * requested it, there is no point in asking again - the firmware must
+     * be confused. 
+     */
+    if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) {
+	if(!(SCpnt->flags & ASKED_FOR_SENSE))
+	    return SUGGEST_SENSE;
+	else
+	    return SUGGEST_RETRY;
+    }
+    
+    SCpnt->flags &= ~ASKED_FOR_SENSE;
+    
+#ifdef DEBUG_INIT
+    printk("scsi%d, channel%d : ", SCpnt->host->host_no, SCpnt->channel);
+    print_sense("", SCpnt);
+    printk("\n");
+#endif
+    if (SCpnt->sense_buffer[2] & 0xe0)
+	return SUGGEST_ABORT;
+    
+    switch (SCpnt->sense_buffer[2] & 0xf)
+    {
+    case NO_SENSE:
+	return 0;
+    case RECOVERED_ERROR:
+	return SUGGEST_IS_OK;
+	
+    case ABORTED_COMMAND:
+	return SUGGEST_RETRY;
+    case NOT_READY:
+    case UNIT_ATTENTION:
+        /*
+         * If we are expecting a CC/UA because of a bus reset that we
+         * performed, treat this just as a retry.  Otherwise this is
+         * information that we should pass up to the upper-level driver
+         * so that we can deal with it there.
+         */
+        if( SCpnt->device->expecting_cc_ua )
+        {
+            SCpnt->device->expecting_cc_ua = 0;
+            return SUGGEST_RETRY;
+        }
+	return SUGGEST_ABORT;
+	
+    /* these three are not supported */
+    case COPY_ABORTED:
+    case VOLUME_OVERFLOW:
+    case MISCOMPARE:
+	
+    case MEDIUM_ERROR:
+	return SUGGEST_REMAP;
+    case BLANK_CHECK:
+    case DATA_PROTECT:
+    case HARDWARE_ERROR:
+    case ILLEGAL_REQUEST:
+    default:
+	return SUGGEST_ABORT;
+    }
+}
+
+/* This function is the mid-level interrupt routine, which decides how
+ *  to handle error conditions.  Each invocation of this function must
+ *  do one and *only* one of the following:
+ *
+ *  (1) Call last_cmnd[host].done.  This is done for fatal errors and
+ *      normal completion, and indicates that the handling for this
+ *      request is complete.
+ *  (2) Call internal_cmnd to requeue the command.  This will result in
+ *      scsi_done being called again when the retry is complete.
+ *  (3) Call scsi_request_sense.  This asks the host adapter/drive for
+ *      more information about the error condition.  When the information
+ *      is available, scsi_done will be called again.
+ *  (4) Call reset().  This is sort of a last resort, and the idea is that
+ *      this may kick things loose and get the drive working again.  reset()
+ *      automatically calls scsi_request_sense, and thus scsi_done will be
+ *      called again once the reset is complete.
+ *
+ *      If none of the above actions are taken, the drive in question
+ *      will hang. If more than one of the above actions are taken by
+ *      scsi_done, then unpredictable behavior will result.
+ */
+static void scsi_done (Scsi_Cmnd * SCpnt)
+{
+    int status=0;
+    int exit=0;
+    int checked;
+    int oldto;
+    struct Scsi_Host * host = SCpnt->host;
+    int result = SCpnt->result;
+    oldto = update_timeout(SCpnt, 0);
+    
+#ifdef DEBUG_TIMEOUT
+    if(result) printk("Non-zero result in scsi_done %x %d:%d\n",
+		      result, SCpnt->target, SCpnt->lun);
+#endif
+    
+    /* If we requested an abort, (and we got it) then fix up the return
+     *  status to say why 
+     */
+    if(host_byte(result) == DID_ABORT && SCpnt->abort_reason)
+	SCpnt->result = result = (result & 0xff00ffff) |
+	    (SCpnt->abort_reason << 16);
+
+
+#define FINISHED 0
+#define MAYREDO  1
+#define REDO     3
+#define PENDING  4
+
+#ifdef DEBUG
+    printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);
+#endif
+
+    if(SCpnt->flags & WAS_SENSE)
+    {
+	SCpnt->use_sg = SCpnt->old_use_sg;
+	SCpnt->cmd_len = SCpnt->old_cmd_len;
+    }
+
+    switch (host_byte(result))
+    {
+    case DID_OK:
+	if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
+	    /* Failed to obtain sense information */
+	{
+	    SCpnt->flags &= ~WAS_SENSE;
+	    SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+	    
+	    if (!(SCpnt->flags & WAS_RESET))
+	    {
+		printk("scsi%d : channel %d target %d lun %d request sense"
+		       " failed, performing reset.\n",
+		       SCpnt->host->host_no, SCpnt->channel, SCpnt->target, 
+		       SCpnt->lun);
+		scsi_reset(SCpnt, FALSE);
+		return;
+	    }
+	    else
+	    {
+		exit = (DRIVER_HARD | SUGGEST_ABORT);
+		status = FINISHED;
+	    }
+	}
+	else switch(msg_byte(result))
+	{
+	case COMMAND_COMPLETE:
+	    switch (status_byte(result))
+	    {
+	    case GOOD:
+		if (SCpnt->flags & WAS_SENSE)
+		{
+#ifdef DEBUG
+		    printk ("In scsi_done, GOOD status, COMMAND COMPLETE, parsing sense information.\n");
+#endif
+		    SCpnt->flags &= ~WAS_SENSE;
+		    SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+		    
+		    switch (checked = check_sense(SCpnt))
+		    {
+		    case SUGGEST_SENSE:
+		    case 0:
+#ifdef DEBUG
+			printk("NO SENSE.  status = REDO\n");
+#endif
+			update_timeout(SCpnt, oldto);
+			status = REDO;
+			break;
+		    case SUGGEST_IS_OK:
+			break;
+		    case SUGGEST_REMAP:
+		    case SUGGEST_RETRY:
+#ifdef DEBUG
+			printk("SENSE SUGGEST REMAP or SUGGEST RETRY - status = MAYREDO\n");
+#endif
+			status = MAYREDO;
+			exit = DRIVER_SENSE | SUGGEST_RETRY;
+			break;
+		    case SUGGEST_ABORT:
+#ifdef DEBUG
+			printk("SENSE SUGGEST ABORT - status = FINISHED");
+#endif
+			status = FINISHED;
+			exit =  DRIVER_SENSE | SUGGEST_ABORT;
+			break;
+		    default:
+			printk ("Internal error %s %d \n", __FILE__,
+				__LINE__);
+		    }
+		}
+		else
+		{
+#ifdef DEBUG
+		    printk("COMMAND COMPLETE message returned, status = FINISHED. \n");
+#endif
+		    exit =  DRIVER_OK;
+		    status = FINISHED;
+		}
+		break;
+		
+	    case CHECK_CONDITION:
+		switch (check_sense(SCpnt))
+		{
+		case 0:
+		    update_timeout(SCpnt, oldto);
+		    status = REDO;
+		    break;
+		case SUGGEST_REMAP:
+		case SUGGEST_RETRY:
+		    status = MAYREDO;
+		    exit = DRIVER_SENSE | SUGGEST_RETRY;
+		    break;
+		case SUGGEST_ABORT:
+		    status = FINISHED;
+		    exit =  DRIVER_SENSE | SUGGEST_ABORT;
+		    break;
+		case SUGGEST_SENSE:
+		    scsi_request_sense (SCpnt);
+		    status = PENDING;
+		    break;
+		}
+		break;
+		
+	    case CONDITION_GOOD:
+	    case INTERMEDIATE_GOOD:
+	    case INTERMEDIATE_C_GOOD:
+		break;
+		
+	    case BUSY:
+		update_timeout(SCpnt, oldto);
+		status = REDO;
+		break;
+		
+	    case RESERVATION_CONFLICT:
+		printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
+		       " reset.\n", SCpnt->host->host_no, SCpnt->channel);
+		scsi_reset(SCpnt, FALSE);
+		return;
+#if 0
+		exit = DRIVER_SOFT | SUGGEST_ABORT;
+		status = MAYREDO;
+		break;
+#endif
+	    default:
+		printk ("Internal error %s %d \n"
+			"status byte = %d \n", __FILE__,
+			__LINE__, status_byte(result));
+		
+	    }
+	    break;
+	default:
+	    panic("scsi: unsupported message byte %d received\n", 
+		  msg_byte(result));
+	}
+	break;
+    case DID_TIME_OUT:
+#ifdef DEBUG
+	printk("Host returned DID_TIME_OUT - ");
+#endif
+	
+	if (SCpnt->flags & WAS_TIMEDOUT)
+	{
+#ifdef DEBUG
+	    printk("Aborting\n");
+#endif
+	    exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
+	}
+	else
+	{
+#ifdef DEBUG
+	    printk ("Retrying.\n");
+#endif
+	    SCpnt->flags  |= WAS_TIMEDOUT;
+	    SCpnt->internal_timeout &= ~IN_ABORT;
+	    status = REDO;
+	}
+	break;
+    case DID_BUS_BUSY:
+    case DID_PARITY:
+	status = REDO;
+	break;
+    case DID_NO_CONNECT:
+#ifdef DEBUG
+	printk("Couldn't connect.\n");
+#endif
+	exit  = (DRIVER_HARD | SUGGEST_ABORT);
+	break;
+    case DID_ERROR:
+	status = MAYREDO;
+	exit = (DRIVER_HARD | SUGGEST_ABORT);
+	break;
+    case DID_BAD_TARGET:
+    case DID_ABORT:
+	exit = (DRIVER_INVALID | SUGGEST_ABORT);
+	break;
+    case DID_RESET:
+	if (SCpnt->flags & IS_RESETTING)
+	{
+	    SCpnt->flags &= ~IS_RESETTING;
+	    status = REDO;
+	    break;
+	}
+	
+	if(msg_byte(result) == GOOD &&
+	   status_byte(result) == CHECK_CONDITION) {
+	    switch (check_sense(SCpnt)) {
+	    case 0:
+		update_timeout(SCpnt, oldto);
+		status = REDO;
+		break;
+	    case SUGGEST_REMAP:
+	    case SUGGEST_RETRY:
+		status = MAYREDO;
+		exit = DRIVER_SENSE | SUGGEST_RETRY;
+		break;
+	    case SUGGEST_ABORT:
+		status = FINISHED;
+		exit =  DRIVER_SENSE | SUGGEST_ABORT;
+		break;
+	    case SUGGEST_SENSE:
+		scsi_request_sense (SCpnt);
+		status = PENDING;
+		break;
+	    }
+	} else {
+	    status=REDO;
+	    exit = SUGGEST_RETRY;
+	}
+	break;
+    default :
+	exit = (DRIVER_ERROR | SUGGEST_DIE);
+    }
+    
+    switch (status)
+    {
+    case FINISHED:
+    case PENDING:
+	break;
+    case MAYREDO:
+#ifdef DEBUG
+	printk("In MAYREDO, allowing %d retries, have %d\n",
+	       SCpnt->allowed, SCpnt->retries);
+#endif
+	if ((++SCpnt->retries) < SCpnt->allowed)
+	{
+	    if ((SCpnt->retries >= (SCpnt->allowed >> 1))
+		&& !(jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
+		&& !(SCpnt->flags & WAS_RESET))
+	    {
+		printk("scsi%d channel %d : resetting for second half of retries.\n",
+		       SCpnt->host->host_no, SCpnt->channel);
+		scsi_reset(SCpnt, FALSE);
+		break;
+	    }
+	    
+	}
+	else
+	{
+	    status = FINISHED;
+	    break;
+	}
+	/* fall through to REDO */
+	
+    case REDO:
+	
+	if (SCpnt->flags & WAS_SENSE)
+	    scsi_request_sense(SCpnt);
+	else
+	{
+	    memcpy ((void *) SCpnt->cmnd,
+		    (void*) SCpnt->data_cmnd,
+		    sizeof(SCpnt->data_cmnd));
+	    SCpnt->request_buffer = SCpnt->buffer;
+	    SCpnt->request_bufflen = SCpnt->bufflen;
+	    SCpnt->use_sg = SCpnt->old_use_sg;
+	    SCpnt->cmd_len = SCpnt->old_cmd_len;
+	    internal_cmnd (SCpnt);
+	}
+	break;
+    default:
+	INTERNAL_ERROR;
+    }
+    
+    if (status == FINISHED) {
+#ifdef DEBUG
+	printk("Calling done function - at address %p\n", SCpnt->done);
+#endif
+	host->host_busy--; /* Indicate that we are free */
+	
+	if (host->block && host->host_busy == 0) {
+	    host_active = NULL;
+	    
+	    /* For block devices "wake_up" is done in end_scsi_request */
+	    if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR &&
+		MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) {
+		struct Scsi_Host * next;
+		
+		for (next = host->block; next != host; next = next->block)
+		    wake_up(&next->host_wait);
+	    }
+	    
+	}
+	
+	wake_up(&host->host_wait);
+	SCpnt->result = result | ((exit & 0xff) << 24);
+	SCpnt->use_sg = SCpnt->old_use_sg;
+	SCpnt->cmd_len = SCpnt->old_cmd_len;
+	SCpnt->done (SCpnt);
+    }
+    
+#undef FINISHED
+#undef REDO
+#undef MAYREDO
+#undef PENDING
+}
+
+/*
+ * The scsi_abort function interfaces with the abort() function of the host
+ * we are aborting, and causes the current command to not complete.  The
+ * caller should deal with any error messages or status returned on the
+ * next call.
+ * 
+ * This will not be called reentrantly for a given host.
+ */
+
+/*
+ * Since we're nice guys and specified that abort() and reset()
+ * can be non-reentrant.  The internal_timeout flags are used for
+ * this.
+ */
+
+
+int scsi_abort (Scsi_Cmnd * SCpnt, int why, int pid)
+{
+    int oldto;
+    unsigned long flags;
+    struct Scsi_Host * host = SCpnt->host;
+    
+    while(1)
+    {
+	save_flags(flags);
+	cli();
+	
+	/*
+	 * Protect against races here.  If the command is done, or we are
+	 * on a different command forget it.
+	 */
+	if (SCpnt->request.rq_status == RQ_INACTIVE || pid != SCpnt->pid) {
+	    restore_flags(flags);
+	    return 0;
+	}
+
+	if (SCpnt->internal_timeout & IN_ABORT)
+	{
+	    restore_flags(flags);
+	    while (SCpnt->internal_timeout & IN_ABORT)
+		barrier();
+	}
+	else
+	{
+	    SCpnt->internal_timeout |= IN_ABORT;
+	    oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
+	    
+	    if ((SCpnt->flags & IS_RESETTING) &&
+		SCpnt->device->soft_reset) {
+		/* OK, this command must have died when we did the
+		 *  reset.  The device itself must have lied. 
+		 */
+		printk("Stale command on %d %d:%d appears to have died when"
+		       " the bus was reset\n", 
+		       SCpnt->channel, SCpnt->target, SCpnt->lun);
+	    }
+	    
+	    restore_flags(flags);
+	    if (!host->host_busy) {
+		SCpnt->internal_timeout &= ~IN_ABORT;
+		update_timeout(SCpnt, oldto);
+		return 0;
+	    }
+	    printk("scsi : aborting command due to timeout : pid %lu, scsi%d,"
+		   " channel %d, id %d, lun %d ",
+		   SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel, 
+		   (int) SCpnt->target, (int) SCpnt->lun);
+	    print_command (SCpnt->cmnd);
+	    if (SCpnt->request.rq_status == RQ_INACTIVE || pid != SCpnt->pid)
+		return 0;
+	    SCpnt->abort_reason = why;
+	    switch(host->hostt->abort(SCpnt)) {
+		/* We do not know how to abort.  Try waiting another
+		 * time increment and see if this helps. Set the
+		 * WAS_TIMEDOUT flag set so we do not try this twice
+		 */
+	    case SCSI_ABORT_BUSY: /* Tough call - returning 1 from
+				   * this is too severe 
+				   */
+	    case SCSI_ABORT_SNOOZE:
+		if(why == DID_TIME_OUT) {
+		    save_flags(flags);
+		    cli();
+		    SCpnt->internal_timeout &= ~IN_ABORT;
+		    if(SCpnt->flags & WAS_TIMEDOUT) {
+			restore_flags(flags);
+			return 1; /* Indicate we cannot handle this.
+				   * We drop down into the reset handler
+				   * and try again 
+				   */
+		    } else {
+			SCpnt->flags |= WAS_TIMEDOUT;
+			oldto = SCpnt->timeout_per_command;
+			update_timeout(SCpnt, oldto);
+		    }
+		    restore_flags(flags);
+		}
+		return 0;
+	    case SCSI_ABORT_PENDING:
+		if(why != DID_TIME_OUT) {
+		    save_flags(flags);
+		    cli();
+		    update_timeout(SCpnt, oldto);
+		    restore_flags(flags);
+		}
+		return 0;
+	    case SCSI_ABORT_SUCCESS:
+		/* We should have already aborted this one.  No
+		 * need to adjust timeout 
+		 */
+	    case SCSI_ABORT_NOT_RUNNING:
+		SCpnt->internal_timeout &= ~IN_ABORT;
+		update_timeout(SCpnt, 0);
+		return 0;
+	    case SCSI_ABORT_ERROR:
+	    default:
+		SCpnt->internal_timeout &= ~IN_ABORT;
+		return 1;
+	    }
+	}
+    }
+}
+
+int scsi_reset (Scsi_Cmnd * SCpnt, int bus_reset_flag)
+{
+    int temp, oldto;
+    unsigned long flags;
+    Scsi_Cmnd * SCpnt1;
+    struct Scsi_Host * host = SCpnt->host;
+
+#ifdef DEBUG
+    printk("Danger Will Robinson! - SCSI bus for host %d is being reset.\n",
+	   host->host_no);
+#endif
+ 
+    /*
+     * First of all, we need to make a recommendation to the low-level
+     * driver as to whether a BUS_DEVICE_RESET should be performed,
+     * or whether we should do a full BUS_RESET.  There is no simple
+     * algorithm here - we basically use a series of heuristics
+     * to determine what we should do.
+     */
+    SCpnt->host->suggest_bus_reset = FALSE;
+    
+    /*
+     * First see if all of the active devices on the bus have
+     * been jammed up so that we are attempting resets.  If so,
+     * then suggest a bus reset.  Forcing a bus reset could
+     * result in some race conditions, but no more than
+     * you would usually get with timeouts.  We will cross
+     * that bridge when we come to it.
+     */
+    SCpnt1 = host->host_queue;
+    while(SCpnt1) {
+	if( SCpnt1->request.rq_status != RQ_INACTIVE
+	   && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 )
+	{
+            break;
+	}
+        SCpnt1 = SCpnt1->next;
+ 	}
+    if( SCpnt1 == NULL ) {
+        SCpnt->host->suggest_bus_reset = TRUE;
+    }
+    
+    
+    /*
+     * If the code that called us is suggesting a hard reset, then
+     * definitely request it.  This usually occurs because a
+     * BUS_DEVICE_RESET times out.
+     */
+    if( bus_reset_flag ) {
+        SCpnt->host->suggest_bus_reset = TRUE;
+    }
+    
+    while (1) {
+	save_flags(flags);
+	cli();
+	if (SCpnt->internal_timeout & IN_RESET)
+	{
+	    restore_flags(flags);
+	    while (SCpnt->internal_timeout & IN_RESET)
+		barrier();
+	}
+	else
+	{
+	    SCpnt->internal_timeout |= IN_RESET;
+	    oldto = update_timeout(SCpnt, RESET_TIMEOUT);
+	    
+	    if (host->host_busy)
+	    {
+		restore_flags(flags);
+		SCpnt1 = host->host_queue;
+		while(SCpnt1) {
+		    if (SCpnt1->request.rq_status != RQ_INACTIVE) {
+#if 0
+			if (!(SCpnt1->flags & IS_RESETTING) &&
+			    !(SCpnt1->internal_timeout & IN_ABORT))
+			    scsi_abort(SCpnt1, DID_RESET, SCpnt->pid);
+#endif
+			SCpnt1->flags |= (WAS_RESET | IS_RESETTING);
+		    }
+		    SCpnt1 = SCpnt1->next;
+		}
+		
+		host->last_reset = jiffies;
+		temp = host->hostt->reset(SCpnt);
+		host->last_reset = jiffies;
+	    }
+	    else
+	    {
+		if (!host->block) host->host_busy++;
+		restore_flags(flags);
+		host->last_reset = jiffies;
+	        SCpnt->flags |= (WAS_RESET | IS_RESETTING);
+		temp = host->hostt->reset(SCpnt);
+		host->last_reset = jiffies;
+		if (!host->block) host->host_busy--;
+	    }
+	    
+#ifdef DEBUG
+	    printk("scsi reset function returned %d\n", temp);
+#endif
+ 
+            if( temp & SCSI_RESET_BUS_RESET )
+            {
+                /*
+                 * The low level driver did a bus reset, so we should
+                 * go through and mark all of the devices on that bus
+                 * as having been reset.
+                 */
+                SCpnt1 = host->host_queue;
+                while(SCpnt1) {
+                    SCpnt1->device->was_reset = 1;
+                    SCpnt1->device->expecting_cc_ua = 1;
+                    SCpnt1 = SCpnt1->next;
+                }
+            }
+            
+            /*
+             * Now figure out what we need to do, based upon
+             * what the low level driver said that it did.
+             */
+            switch(temp & SCSI_RESET_ACTION) {
+	    case SCSI_RESET_SUCCESS:
+		save_flags(flags);
+		cli();
+		SCpnt->internal_timeout &= ~IN_RESET;
+		update_timeout(SCpnt, oldto);
+		restore_flags(flags);
+		return 0;
+	    case SCSI_RESET_PENDING:
+		return 0;
+	    case SCSI_RESET_PUNT:
+                SCpnt->internal_timeout &= ~IN_RESET;
+                scsi_request_sense (SCpnt);
+                return 0;
+	    case SCSI_RESET_WAKEUP:
+		SCpnt->internal_timeout &= ~IN_RESET;
+		scsi_request_sense (SCpnt);
+                /*
+                 * Since a bus reset was performed, we
+                 * need to wake up each and every command
+                 * that was active on the bus.
+                 */
+                if( temp & SCSI_RESET_BUS_RESET )
+                {
+                    SCpnt1 = host->host_queue;
+                    while(SCpnt1) {
+                        if( SCpnt->request.rq_status != RQ_INACTIVE
+                           && SCpnt1 != SCpnt)
+                            scsi_request_sense (SCpnt);
+                        SCpnt1 = SCpnt1->next;
+                    }
+                }
+		return 0;
+	    case SCSI_RESET_SNOOZE:
+		/* In this case, we set the timeout field to 0
+		 * so that this command does not time out any more,
+		 * and we return 1 so that we get a message on the
+		 * screen. 
+		 */
+		save_flags(flags);
+		cli();
+		SCpnt->internal_timeout &= ~IN_RESET;
+		update_timeout(SCpnt, 0);
+		restore_flags(flags);
+		/* If you snooze, you lose... */
+	    case SCSI_RESET_ERROR:
+	    default:
+		return 1;
+	    }
+	    
+	    return temp;
+	}
+    }
+}
+
+
+static void scsi_main_timeout(void)
+{
+    /*
+     * We must not enter update_timeout with a timeout condition still pending.
+     */
+    
+    int timed_out, pid;
+    unsigned long flags;
+    struct Scsi_Host * host;
+    Scsi_Cmnd * SCpnt = NULL;
+    
+    do {
+	save_flags(flags);
+	cli();
+	
+	update_timeout(NULL, 0);
+	/*
+	 * Find all timers such that they have 0 or negative (shouldn't happen)
+	 * time remaining on them.
+	 */
+	
+	timed_out = 0;
+	for(host = scsi_hostlist; host; host = host->next) {
+	    for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+		if (SCpnt->timeout == -1)
+		{
+		    SCpnt->timeout = 0;
+		    pid = SCpnt->pid;
+		    restore_flags(flags);
+		    scsi_times_out(SCpnt, pid);
+		    ++timed_out;
+		    save_flags(flags);
+		    cli();
+		}
+	}
+    } while (timed_out);
+    restore_flags(flags);
+}
+
+/*
+ * The strategy is to cause the timer code to call scsi_times_out()
+ * when the soonest timeout is pending.
+ * The arguments are used when we are queueing a new command, because
+ * we do not want to subtract the time used from this time, but when we
+ * set the timer, we want to take this value into account.
+ */
+
+static int update_timeout(Scsi_Cmnd * SCset, int timeout)
+{
+    unsigned int least, used;
+    unsigned int oldto;
+    unsigned long flags;
+    struct Scsi_Host * host;
+    Scsi_Cmnd * SCpnt = NULL;
+
+    save_flags(flags);
+    cli();
+
+    /*
+     * Figure out how much time has passed since the last time the timeouts
+     * were updated
+     */
+    used = (time_start) ? (jiffies - time_start) : 0;
+
+    /*
+     * Find out what is due to timeout soonest, and adjust all timeouts for
+     * the amount of time that has passed since the last time we called
+     * update_timeout.
+     */
+
+    oldto = 0;
+    
+    if(SCset){
+	oldto = SCset->timeout - used;
+	SCset->timeout = timeout + used;
+    }
+
+    least = 0xffffffff;
+    
+    for(host = scsi_hostlist; host; host = host->next)
+	for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+	    if (SCpnt->timeout > 0) {
+		SCpnt->timeout -= used;
+		if(SCpnt->timeout <= 0) SCpnt->timeout = -1;
+		if(SCpnt->timeout > 0 && SCpnt->timeout < least)
+		    least = SCpnt->timeout;
+	    }
+    
+    /*
+     * If something is due to timeout again, then we will set the next timeout
+     * interrupt to occur.  Otherwise, timeouts are disabled.
+     */
+    
+    if (least != 0xffffffff)
+    {
+	time_start = jiffies;
+	timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
+	timer_active |= 1 << SCSI_TIMER;
+    }
+    else
+    {
+	timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0;
+	timer_active &= ~(1 << SCSI_TIMER);
+    }
+    restore_flags(flags);
+    return oldto;
+}
+
+#define MALLOC_PAGEBITS 12
+
+static int scsi_register_host(Scsi_Host_Template *);
+static void scsi_unregister_host(Scsi_Host_Template *);
+
+void *scsi_malloc(unsigned int len)
+{
+    unsigned int nbits, mask;
+    unsigned long flags;
+    int i, j;
+    if((len & 0x1ff) || len > (1<<MALLOC_PAGEBITS))
+	return NULL;
+    
+    save_flags(flags);
+    cli();
+    nbits = len >> 9;
+    mask = (1 << nbits) - 1;
+    
+    for(i=0;i < (dma_sectors >> (MALLOC_PAGEBITS - 9)); i++)
+	for(j=0; j<=(sizeof(*dma_malloc_freelist) * 8) - nbits; j++){
+	    if ((dma_malloc_freelist[i] & (mask << j)) == 0){
+		dma_malloc_freelist[i] |= (mask << j);
+		restore_flags(flags);
+		dma_free_sectors -= nbits;
+#ifdef DEBUG
+		printk("SMalloc: %d %p ",len, dma_malloc_pages[i] + (j << 9));
+#endif
+		return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
+	    }
+	}
+    restore_flags(flags);
+    return NULL;  /* Nope.  No more */
+}
+
+int scsi_free(void *obj, unsigned int len)
+{
+    int page, sector, nbits, mask;
+    long offset;
+    unsigned long flags;
+    
+#ifdef DEBUG
+    printk("Sfree %p %d\n",obj, len);
+#endif
+    
+    offset = -1;
+    for (page = 0; page < (dma_sectors >> 3); page++)
+	if ((unsigned long) obj >= (unsigned long) dma_malloc_pages[page] &&
+	    (unsigned long) obj < (unsigned long) dma_malloc_pages[page] 
+	    + (1 << MALLOC_PAGEBITS))
+	{
+	    offset = ((unsigned long) obj) - ((unsigned long)dma_malloc_pages[page]);
+	    break;
+	}
+    
+    if (page == (dma_sectors >> 3)) panic("Bad offset");
+    sector = offset >> 9;
+    if(sector >= dma_sectors) panic ("Bad page");
+    
+    sector = (offset >> 9) & (sizeof(*dma_malloc_freelist) * 8 - 1);
+    nbits = len >> 9;
+    mask = (1 << nbits) - 1;
+    
+    if ((mask << sector) > 0xffff) panic ("Bad memory alignment");
+    
+    save_flags(flags);
+    cli();
+    if((dma_malloc_freelist[page] & (mask << sector)) != (mask<<sector))
+	panic("Trying to free unused memory");
+    
+    dma_free_sectors += nbits;
+    dma_malloc_freelist[page] &= ~(mask << sector);
+    restore_flags(flags);
+    return 0;
+}
+
+
+int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
+
+void * scsi_init_malloc(unsigned int size, int priority)
+{
+    void * retval;
+    
+    /*
+     * For buffers used by the DMA pool, we assume page aligned 
+     * structures.
+     */
+    if ((size % PAGE_SIZE) == 0) {
+	int order, a_size;
+	for (order = 0, a_size = PAGE_SIZE;
+             a_size < size; order++, a_size <<= 1)
+            ;
+        retval = (void *) __get_dma_pages(priority & GFP_LEVEL_MASK,
+	                                            order);
+    } else
+        retval = kmalloc(size, priority);
+
+    if (retval)
+	memset(retval, 0, size);
+    return retval;
+}
+
+
+void scsi_init_free(char * ptr, unsigned int size)
+{ 
+    /*
+     * We need this special code here because the DMA pool assumes
+     * page aligned data.  Besides, it is wasteful to allocate
+     * page sized chunks with kmalloc.
+     */
+    if ((size % PAGE_SIZE) == 0) {
+    	int order, a_size;
+
+	for (order = 0, a_size = PAGE_SIZE;
+	     a_size < size; order++, a_size <<= 1)
+	    ;
+	free_pages((unsigned long)ptr, order);
+    } else
+	kfree(ptr);
+}
+
+void scsi_build_commandblocks(Scsi_Device * SDpnt)
+{
+    int j;
+    Scsi_Cmnd * SCpnt;
+    struct Scsi_Host * host = NULL;
+    
+    for(j=0;j<SDpnt->host->cmd_per_lun;j++){
+	SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+	SCpnt->host = SDpnt->host;
+	SCpnt->device = SDpnt;
+	SCpnt->target = SDpnt->id;
+	SCpnt->lun = SDpnt->lun;
+	SCpnt->channel = SDpnt->channel;
+	SCpnt->request.rq_status = RQ_INACTIVE;
+	SCpnt->use_sg = 0;
+	SCpnt->old_use_sg = 0;
+	SCpnt->old_cmd_len = 0;
+	SCpnt->timeout = 0;
+	SCpnt->underflow = 0;
+	SCpnt->transfersize = 0;
+	SCpnt->host_scribble = NULL;
+	host = SDpnt->host;
+	if(host->host_queue)
+	    host->host_queue->prev = SCpnt;
+	SCpnt->next = host->host_queue;
+	SCpnt->prev = NULL;
+	host->host_queue = SCpnt;
+    }
+    SDpnt->has_cmdblocks = 1;
+}
+
+/*
+ * scsi_dev_init() is our initialization routine, which in turn calls host
+ * initialization, bus scanning, and sd/st initialization routines. 
+ */
+
+int scsi_dev_init(void)
+{
+    Scsi_Device * SDpnt;
+    struct Scsi_Host * shpnt;
+    struct Scsi_Device_Template * sdtpnt;
+#ifdef FOO_ON_YOU
+    return;
+#endif
+
+    /* Yes we're here... */
+    dispatch_scsi_info_ptr = dispatch_scsi_info;
+
+    /* Init a few things so we can "malloc" memory. */
+    scsi_loadable_module_flag = 0;
+    
+    timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+    timer_table[SCSI_TIMER].expires = 0;
+
+
+    /* Register the core /proc/scsi entry */
+#if CONFIG_PROC_FS 
+    proc_scsi_register(0, &proc_scsi_scsi);    
+#endif
+
+    /* initialize all hosts */
+    scsi_init();
+
+    scsi_devices = (Scsi_Device *) NULL;
+
+    for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+	scan_scsis(shpnt,0,0,0,0);           /* scan for scsi devices */
+
+    printk("scsi : detected ");
+    for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+	if (sdtpnt->dev_noticed && sdtpnt->name)
+	    printk("%d SCSI %s%s ", sdtpnt->dev_noticed, sdtpnt->name,
+		   (sdtpnt->dev_noticed != 1) ? "s" : "");
+    printk("total.\n");
+    
+    for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+	if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+    for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+	SDpnt->scsi_request_fn = NULL;
+	for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+	    if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+	if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+    }
+    
+
+    /*
+     * This should build the DMA pool.
+     */
+    resize_dma_pool();
+
+    /*
+     * OK, now we finish the initialization by doing spin-up, read
+     * capacity, etc, etc 
+     */
+    for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+	if(sdtpnt->finish && sdtpnt->nr_dev)
+	    (*sdtpnt->finish)();
+
+    scsi_loadable_module_flag = 1;
+
+    return 0;
+}
+
+static void print_inquiry(unsigned char *data)
+{
+    int i;
+    
+    printk("  Vendor: ");
+    for (i = 8; i < 16; i++)
+    {
+	if (data[i] >= 0x20 && i < data[4] + 5)
+	    printk("%c", data[i]);
+	else
+	    printk(" ");
+    }
+    
+    printk("  Model: ");
+    for (i = 16; i < 32; i++)
+    {
+	if (data[i] >= 0x20 && i < data[4] + 5)
+	    printk("%c", data[i]);
+	else
+	    printk(" ");
+    }
+    
+    printk("  Rev: ");
+    for (i = 32; i < 36; i++)
+    {
+	if (data[i] >= 0x20 && i < data[4] + 5)
+	    printk("%c", data[i]);
+	else
+	    printk(" ");
+    }
+    
+    printk("\n");
+    
+    i = data[0] & 0x1f;
+    
+    printk("  Type:   %s ",
+	   i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown          " );
+    printk("                 ANSI SCSI revision: %02x", data[2] & 0x07);
+    if ((data[2] & 7) >= 2 && (data[7] & 2))
+    	printk (" TAG\n");
+    if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
+	printk(" CCS\n");
+    else
+	printk("\n");
+}
+
+
+#ifdef CONFIG_PROC_FS
+int scsi_proc_info(char *buffer, char **start, off_t offset, int length, 
+		    int hostno, int inout)
+{
+    Scsi_Device *scd;
+    struct Scsi_Host *HBA_ptr;
+    int  parameter[4];
+    char *p;
+    int	  size, len = 0;
+    off_t begin = 0;
+    off_t pos = 0;
+
+    scd = scsi_devices;
+    HBA_ptr = scsi_hostlist;
+
+    if(inout == 0) { 
+	size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+	len += size; 
+	pos = begin + len;
+	while (HBA_ptr) {
+#if 0
+	    size += sprintf(buffer+len,"scsi%2d: %s\n", (int) HBA_ptr->host_no, 
+	                    HBA_ptr->hostt->procname);
+	    len += size; 
+	    pos = begin + len;
+#endif
+	    scd = scsi_devices;
+	    while (scd) {
+		if (scd->host == HBA_ptr) {
+		    proc_print_scsidevice(scd, buffer, &size, len);
+		    len += size; 
+		    pos = begin + len;
+		    
+		    if (pos < offset) {
+			len = 0;
+			begin = pos;
+		    }
+		    if (pos > offset + length)
+			goto stop_output;
+		}
+		scd = scd->next;
+	    }
+	    HBA_ptr = HBA_ptr->next;
+	}
+	
+    stop_output:
+	*start=buffer+(offset-begin);   /* Start of wanted data */
+	len-=(offset-begin);	        /* Start slop */
+	if(len>length)
+	    len = length;		/* Ending slop */
+	return (len);     
+    }
+
+    if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
+	return(-EINVAL);
+
+    if(!strncmp("singledevice", buffer + 5, 12)) {
+	p = buffer + 17;
+
+	parameter[0] = simple_strtoul(p , &p, 0);
+	parameter[1] = simple_strtoul(p , &p, 0);
+	parameter[2] = simple_strtoul(p , &p, 0);
+	parameter[3] = simple_strtoul(p , &p, 0);
+
+	while(scd && scd->host->host_no != parameter[0] 
+	      && scd->channel != parameter[1] 
+	      && scd->id != parameter[2] 
+	      && scd->lun != parameter[3]) {
+	    scd = scd->next;
+	}
+	if(scd)
+	    return(-ENOSYS);  /* We do not yet support unplugging */
+	while(HBA_ptr && HBA_ptr->host_no != parameter[0])
+	    HBA_ptr = HBA_ptr->next;
+
+	if(!HBA_ptr)
+	    return(-ENXIO);
+
+	scan_scsis (HBA_ptr, 1, parameter[1], parameter[2], parameter[3]);
+	return(0);
+    }
+    return(-EINVAL);
+}
+#endif
+
+/*
+ * Go through the device list and recompute the most appropriate size
+ * for the dma pool.  Then grab more memory (as required).
+ */
+static void resize_dma_pool(void)
+{
+    int i;
+    struct Scsi_Host * shpnt;
+    struct Scsi_Host * host = NULL;
+    Scsi_Device * SDpnt;
+    unsigned long flags;
+    unsigned char * new_dma_malloc_freelist = NULL;
+    unsigned int new_dma_sectors = 0;
+    unsigned int new_need_isa_buffer = 0;
+    unsigned char ** new_dma_malloc_pages = NULL;
+
+    if( !scsi_devices )
+    {
+	/*
+	 * Free up the DMA pool.
+	 */
+	if( dma_free_sectors != dma_sectors )
+	    panic("SCSI DMA pool memory leak\n");
+
+	for(i=0; i < dma_sectors >> 3; i++)
+	    scsi_init_free(dma_malloc_pages[i], PAGE_SIZE);
+	if (dma_malloc_pages)
+	    scsi_init_free((char *) dma_malloc_pages, dma_sectors>>1);
+	dma_malloc_pages = NULL;
+	if (dma_malloc_freelist)
+	    scsi_init_free(dma_malloc_freelist, dma_sectors>>3);
+	dma_malloc_freelist = NULL;
+	dma_sectors = 0;
+	dma_free_sectors = 0;
+	return;
+    }
+    /* Next, check to see if we need to extend the DMA buffer pool */
+	
+    new_dma_sectors = 16;  /* Base value we use */
+
+    if (high_memory-1 > ISA_DMA_THRESHOLD)
+	scsi_need_isa_bounce_buffers = 1;
+    else
+	scsi_need_isa_bounce_buffers = 0;
+    
+    if (scsi_devicelist)
+	for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+	    new_dma_sectors += 8;  /* Increment for each host */
+    
+    for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+	host = SDpnt->host;
+	
+	if(SDpnt->type != TYPE_TAPE)
+	    new_dma_sectors += ((host->sg_tablesize *
+	                         sizeof(struct scatterlist) + 511) >> 9) *
+	                             host->cmd_per_lun;
+	
+	if(host->unchecked_isa_dma &&
+	   scsi_need_isa_bounce_buffers &&
+	   SDpnt->type != TYPE_TAPE) {
+	    new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+	        host->cmd_per_lun;
+	    new_need_isa_buffer++;
+	}
+    }
+    
+    new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
+    
+    /*
+     * We never shrink the buffers - this leads to
+     * race conditions that I would rather not even think
+     * about right now.
+     */
+    if( new_dma_sectors < dma_sectors )
+	new_dma_sectors = dma_sectors;
+    
+    if (new_dma_sectors)
+    {
+	new_dma_malloc_freelist = (unsigned char *)
+	    scsi_init_malloc(new_dma_sectors >> 3, GFP_ATOMIC);
+	memset(new_dma_malloc_freelist, 0, new_dma_sectors >> 3);
+	
+	new_dma_malloc_pages = (unsigned char **)
+	    scsi_init_malloc(new_dma_sectors >> 1, GFP_ATOMIC);
+	memset(new_dma_malloc_pages, 0, new_dma_sectors >> 1);
+    }
+    
+    /*
+     * If we need more buffers, expand the list.
+     */
+    if( new_dma_sectors > dma_sectors ) { 
+	for(i=dma_sectors >> 3; i< new_dma_sectors >> 3; i++)
+	    new_dma_malloc_pages[i] = (unsigned char *)
+	        scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+    }
+    
+    /* When we dick with the actual DMA list, we need to 
+     * protect things 
+     */
+    save_flags(flags);
+    cli();
+    if (dma_malloc_freelist)
+    {
+	memcpy(new_dma_malloc_freelist, dma_malloc_freelist, dma_sectors >> 3);
+	scsi_init_free(dma_malloc_freelist, dma_sectors>>3);
+    }
+    dma_malloc_freelist = new_dma_malloc_freelist;
+    
+    if (dma_malloc_pages)
+    {
+	memcpy(new_dma_malloc_pages, dma_malloc_pages, dma_sectors >> 1);
+	scsi_init_free((char *) dma_malloc_pages, dma_sectors>>1);
+    }
+    
+    dma_free_sectors += new_dma_sectors - dma_sectors;
+    dma_malloc_pages = new_dma_malloc_pages;
+    dma_sectors = new_dma_sectors;
+    need_isa_buffer = new_need_isa_buffer;
+    restore_flags(flags);
+}
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a low level scsi driver to the system.
+ */
+static int scsi_register_host(Scsi_Host_Template * tpnt)
+{
+    int pcount;
+    struct Scsi_Host * shpnt;
+    Scsi_Device * SDpnt;
+    struct Scsi_Device_Template * sdtpnt;
+    const char * name;
+    
+    if (tpnt->next || !tpnt->detect) return 1;/* Must be already loaded, or
+					       * no detect routine available 
+					       */
+    pcount = next_scsi_host;
+    if ((tpnt->present = tpnt->detect(tpnt)))
+    {
+	if(pcount == next_scsi_host) {
+	    if(tpnt->present > 1) {
+		printk("Failure to register low-level scsi driver");
+		scsi_unregister_host(tpnt);
+		return 1;
+	    }
+	    /* The low-level driver failed to register a driver.  We
+	     *  can do this now. 
+	     */
+	    scsi_register(tpnt,0);
+	}
+	tpnt->next = scsi_hosts; /* Add to the linked list */
+	scsi_hosts = tpnt;
+	
+	/* Add the new driver to /proc/scsi */
+#if CONFIG_PROC_FS 
+	build_proc_dir_entries(tpnt);
+#endif
+	
+	for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+	    if(shpnt->hostt == tpnt)
+	    {
+		if(tpnt->info)
+		    name = tpnt->info(shpnt);
+		else
+		    name = tpnt->name;
+		printk ("scsi%d : %s\n", /* And print a little message */
+			shpnt->host_no, name);
+	    }
+	
+	printk ("scsi : %d host%s.\n", next_scsi_host,
+		(next_scsi_host == 1) ? "" : "s");
+	
+	scsi_make_blocked_list();
+	
+	/* The next step is to call scan_scsis here.  This generates the
+	 * Scsi_Devices entries 
+	 */
+	
+	for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+	    if(shpnt->hostt == tpnt) scan_scsis(shpnt,0,0,0,0);
+	
+	for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+	    if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+	
+	/* Next we create the Scsi_Cmnd structures for this host */
+	
+	for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+	    if(SDpnt->host->hostt == tpnt)
+	    {
+		for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+		    if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+		if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+	    }
+	
+	/*
+	 * Now that we have all of the devices, resize the DMA pool,
+	 * as required.  */
+	resize_dma_pool();
+
+
+	/* This does any final handling that is required. */
+	for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+	    if(sdtpnt->finish && sdtpnt->nr_dev)
+		(*sdtpnt->finish)();
+    }
+    
+#if defined(USE_STATIC_SCSI_MEMORY)
+    printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+	    (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+	    (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+	    (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+	
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+/*
+ * Similarly, this entry point should be called by a loadable module if it
+ * is trying to remove a low level scsi driver from the system.
+ */
+static void scsi_unregister_host(Scsi_Host_Template * tpnt)
+{
+    Scsi_Host_Template * SHT, *SHTp;
+    Scsi_Device *sdpnt, * sdppnt, * sdpnt1;
+    Scsi_Cmnd * SCpnt;
+    unsigned long flags;
+    struct Scsi_Device_Template * sdtpnt;
+    struct Scsi_Host * shpnt, *sh1;
+    int pcount;
+    
+    /* First verify that this host adapter is completely free with no pending
+     * commands */
+    
+    for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+	if(sdpnt->host->hostt == tpnt && sdpnt->host->hostt->usage_count
+	   && *sdpnt->host->hostt->usage_count) return;
+    
+    for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+    {
+	if (shpnt->hostt != tpnt) continue;
+	for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+	{
+	    save_flags(flags);
+	    cli();
+	    if(SCpnt->request.rq_status != RQ_INACTIVE) {
+		restore_flags(flags);
+		for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+		    if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING)
+			SCpnt->request.rq_status = RQ_INACTIVE;
+		printk("Device busy???\n");
+		return;
+	    }
+	    SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING;  /* Mark as busy */
+	    restore_flags(flags);
+	}
+    }
+    /* Next we detach the high level drivers from the Scsi_Device structures */
+    
+    for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+	if(sdpnt->host->hostt == tpnt)
+	{
+	    for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+		if(sdtpnt->detach) (*sdtpnt->detach)(sdpnt);
+	    /* If something still attached, punt */
+	    if (sdpnt->attached) {
+		printk("Attached usage count = %d\n", sdpnt->attached);
+		return;
+	    }
+	}
+    
+    /* Next we free up the Scsi_Cmnd structures for this host */
+    
+    for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+	if(sdpnt->host->hostt == tpnt)
+	    while (sdpnt->host->host_queue) {
+		SCpnt = sdpnt->host->host_queue->next;
+		scsi_init_free((char *) sdpnt->host->host_queue, sizeof(Scsi_Cmnd));
+		sdpnt->host->host_queue = SCpnt;
+		if (SCpnt) SCpnt->prev = NULL;
+		sdpnt->has_cmdblocks = 0;
+	    }
+    
+    /* Next free up the Scsi_Device structures for this host */
+    
+    sdppnt = NULL;
+    for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt1)
+    {
+	sdpnt1 = sdpnt->next;
+	if (sdpnt->host->hostt == tpnt) {
+	    if (sdppnt)
+		sdppnt->next = sdpnt->next;
+	    else
+		scsi_devices = sdpnt->next;
+	    scsi_init_free((char *) sdpnt, sizeof (Scsi_Device));
+	} else
+	    sdppnt = sdpnt;
+    }
+    
+    /* Next we go through and remove the instances of the individual hosts
+     * that were detected */
+    
+    shpnt = scsi_hostlist;
+    while(shpnt) {
+	sh1 = shpnt->next;
+	if(shpnt->hostt == tpnt) {
+	    if(shpnt->loaded_as_module) {
+		pcount = next_scsi_host;
+	        /* Remove the /proc/scsi directory entry */
+#if CONFIG_PROC_FS 
+	        proc_scsi_unregister(tpnt->proc_dir, 
+	                             shpnt->host_no + PROC_SCSI_FILE);
+#endif   
+		if(tpnt->release)
+		    (*tpnt->release)(shpnt);
+		else {
+		    /* This is the default case for the release function.  
+		     * It should do the right thing for most correctly 
+		     * written host adapters. 
+		     */
+		    if (shpnt->irq) free_irq(shpnt->irq);
+		    if (shpnt->dma_channel != 0xff) free_dma(shpnt->dma_channel);
+		    if (shpnt->io_port && shpnt->n_io_port)
+			release_region(shpnt->io_port, shpnt->n_io_port);
+		}
+		if(pcount == next_scsi_host) scsi_unregister(shpnt);
+		tpnt->present--;
+	    }
+	}
+	shpnt = sh1;
+    }
+    
+    /*
+     * If there are absolutely no more hosts left, it is safe
+     * to completely nuke the DMA pool.  The resize operation will
+     * do the right thing and free everything.
+     */
+    if( !scsi_devices )
+	resize_dma_pool();
+
+    printk ("scsi : %d host%s.\n", next_scsi_host,
+	    (next_scsi_host == 1) ? "" : "s");
+    
+#if defined(USE_STATIC_SCSI_MEMORY)
+    printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+	    (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+	    (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+	    (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+    
+    scsi_make_blocked_list();
+    
+    /* There were some hosts that were loaded at boot time, so we cannot
+       do any more than this */
+    if (tpnt->present) return;
+    
+    /* OK, this is the very last step.  Remove this host adapter from the
+       linked list. */
+    for(SHTp=NULL, SHT=scsi_hosts; SHT; SHTp=SHT, SHT=SHT->next)
+	if(SHT == tpnt) {
+	    if(SHTp)
+		SHTp->next = SHT->next;
+	    else
+		scsi_hosts = SHT->next;
+	    SHT->next = NULL;
+	    break;
+	}
+    
+    /* Rebuild the /proc/scsi directory entries */
+#if CONFIG_PROC_FS 
+    proc_scsi_unregister(tpnt->proc_dir, tpnt->proc_dir->low_ino);
+#endif
+    MOD_DEC_USE_COUNT;
+}
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a high level scsi driver to the system.
+ */
+static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
+{
+    Scsi_Device * SDpnt;
+    
+    if (tpnt->next) return 1;
+    
+    scsi_register_device(tpnt);
+    /*
+     * First scan the devices that we know about, and see if we notice them.
+     */
+    
+    for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+	if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt);
+    
+    /*
+     * If any of the devices would match this driver, then perform the
+     * init function.
+     */
+    if(tpnt->init && tpnt->dev_noticed)
+	if ((*tpnt->init)()) return 1;
+    
+    /*
+     * Now actually connect the devices to the new driver.
+     */
+    for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+    {
+	if(tpnt->attach)  (*tpnt->attach)(SDpnt);
+	/*
+	 * If this driver attached to the device, and we no longer
+	 * have anything attached, release the scso command blocks.
+	 */
+	if(SDpnt->attached && SDpnt->has_cmdblocks == 0)
+	    scsi_build_commandblocks(SDpnt);
+    }
+    
+    /*
+     * This does any final handling that is required. 
+     */
+    if(tpnt->finish && tpnt->nr_dev)  (*tpnt->finish)();
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int scsi_unregister_device(struct Scsi_Device_Template * tpnt)
+{
+    Scsi_Device * SDpnt;
+    Scsi_Cmnd * SCpnt;
+    struct Scsi_Device_Template * spnt;
+    struct Scsi_Device_Template * prev_spnt;
+    
+    /*
+     * If we are busy, this is not going to fly.
+     */
+    if( *tpnt->usage_count != 0) return 0;
+    /*
+     * Next, detach the devices from the driver.
+     */
+    
+    for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+    {
+	if(tpnt->detach) (*tpnt->detach)(SDpnt);
+	if(SDpnt->attached == 0)
+	{
+	    /*
+	     * Nobody is using this device any more.  Free all of the
+	     * command structures.
+	     */
+	    for(SCpnt = SDpnt->host->host_queue; SCpnt; SCpnt = SCpnt->next)
+	    {
+		if(SCpnt->device == SDpnt)
+		{
+		    if(SCpnt->prev != NULL)
+			SCpnt->prev->next = SCpnt->next;
+		    if(SCpnt->next != NULL)
+			SCpnt->next->prev = SCpnt->prev;
+		    if(SCpnt == SDpnt->host->host_queue)
+			SDpnt->host->host_queue = SCpnt->next;
+		    scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
+		}
+	    }
+	    SDpnt->has_cmdblocks = 0;
+	}
+    }
+    /*
+     * Extract the template from the linked list.
+     */
+    spnt = scsi_devicelist;
+    prev_spnt = NULL;
+    while(spnt != tpnt)
+    {
+	prev_spnt = spnt;
+	spnt = spnt->next;
+    }
+    if(prev_spnt == NULL)
+	scsi_devicelist = tpnt->next;
+    else
+	prev_spnt->next = spnt->next;
+    
+    MOD_DEC_USE_COUNT;
+    /*
+     * Final cleanup for the driver is done in the driver sources in the 
+     * cleanup function.
+     */
+    return 0;
+}
+
+
+int scsi_register_module(int module_type, void * ptr)
+{
+    switch(module_type){
+    case MODULE_SCSI_HA:
+	return scsi_register_host((Scsi_Host_Template *) ptr);
+	
+	/* Load upper level device handler of some kind */
+    case MODULE_SCSI_DEV:
+	return scsi_register_device_module((struct Scsi_Device_Template *) ptr);
+	/* The rest of these are not yet implemented */
+	
+	/* Load constants.o */
+    case MODULE_SCSI_CONST:
+	
+	/* Load specialized ioctl handler for some device.  Intended for 
+	 * cdroms that have non-SCSI2 audio command sets. */
+    case MODULE_SCSI_IOCTL:
+	
+    default:
+	return 1;
+    }
+}
+
+void scsi_unregister_module(int module_type, void * ptr)
+{
+    switch(module_type) {
+    case MODULE_SCSI_HA:
+	scsi_unregister_host((Scsi_Host_Template *) ptr);
+	break;
+    case MODULE_SCSI_DEV:
+	scsi_unregister_device((struct Scsi_Device_Template *) ptr);
+	break;
+	/* The rest of these are not yet implemented. */
+    case MODULE_SCSI_CONST:
+    case MODULE_SCSI_IOCTL:
+	break;
+    default:
+    }
+    return;
+}
+
+#ifdef DEBUG_TIMEOUT
+static void
+scsi_dump_status(void)
+{
+    int i;
+    struct Scsi_Host * shpnt;
+    Scsi_Cmnd * SCpnt;
+    printk("Dump of scsi parameters:\n");
+    i = 0;
+    for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+	for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+	{
+	    /*  (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x      */
+	    printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %ld) (%d %d %x) (%d %d %d) %x %x %x\n",
+		   i++, SCpnt->host->host_no,
+		   SCpnt->channel,
+		   SCpnt->target,
+		   SCpnt->lun,
+		   kdevname(SCpnt->request.rq_dev),
+		   SCpnt->request.sector,
+		   SCpnt->request.nr_sectors,
+		   SCpnt->request.current_nr_sectors,
+		   SCpnt->use_sg,
+		   SCpnt->retries,
+		   SCpnt->allowed,
+		   SCpnt->flags,
+		   SCpnt->timeout_per_command,
+		   SCpnt->timeout,
+		   SCpnt->internal_timeout,
+		   SCpnt->cmnd[0],
+		   SCpnt->sense_buffer[2],
+		   SCpnt->result);
+	}
+    printk("wait_for_request = %p\n", wait_for_request);
+    /* Now dump the request lists for each block device */
+    printk("Dump of pending block device requests\n");
+    for(i=0; i<MAX_BLKDEV; i++)
+	if(blk_dev[i].current_request)
+	{
+	    struct request * req;
+	    printk("%d: ", i);
+	    req = blk_dev[i].current_request;
+	    while(req) {
+		printk("(%s %d %ld %ld %ld) ",
+		       kdevname(req->rq_dev),
+		       req->cmd,
+		       req->sector,
+		       req->nr_sectors,
+		       req->current_nr_sectors);
+		req = req->next;
+	    }
+	    printk("\n");
+	}
+}
+#endif
+
+#ifdef MODULE
+
+
+char kernel_version[] = UTS_RELEASE;
+
+extern struct symbol_table scsi_symbol_table;
+
+
+int init_module(void) {
+    /*
+     * This makes /proc/scsi visible.
+     */
+    dispatch_scsi_info_ptr = dispatch_scsi_info;
+
+    timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+    timer_table[SCSI_TIMER].expires = 0;
+    register_symtab(&scsi_symbol_table);
+    scsi_loadable_module_flag = 1;
+    
+    dma_sectors = PAGE_SIZE / 512;
+    /*
+     * Set up a minimal DMA buffer list - this will be used during scan_scsis
+     * in some cases.
+     */
+    
+    /* One bit per sector to indicate free/busy */
+    dma_malloc_freelist = (unsigned char *)
+	scsi_init_malloc(dma_sectors >> 3, GFP_ATOMIC);
+    memset(dma_malloc_freelist, 0, dma_sectors >> 3);
+    
+    /* One pointer per page for the page list */
+    dma_malloc_pages = (unsigned char **)
+	scsi_init_malloc(dma_sectors >> 1, GFP_ATOMIC);
+    dma_malloc_pages[0] = (unsigned char *)
+	scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+    return 0;
+}
+
+void cleanup_module( void) 
+{
+    int i;
+    
+    if (MOD_IN_USE) {
+	printk(KERN_INFO __FILE__ ": module is in use, remove rejected\n");
+	return;
+    }
+    
+    /* No, we're not here anymore. Don't show the /proc/scsi files. */
+    dispatch_scsi_info_ptr = 0L;
+
+    /*
+     * Free up the DMA pool.
+     */
+    resize_dma_pool();
+
+    timer_table[SCSI_TIMER].fn = NULL;
+    timer_table[SCSI_TIMER].expires = 0;
+    /*
+     * Supposedly you just do this, and the last symtab registered will
+     * be removed.  If someone else registered a symtab, this could
+     * blow up in our faces.  FIXME.
+     */
+    register_symtab(0);
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff -urNwbB linux/arch/arm/drivers/scsi/scsi_module.c linux.arm/arch/arm/drivers/scsi/scsi_module.c
--- linux/arch/arm/drivers/scsi/scsi_module.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsi_module.c	Sun Feb 11 09:32:33 1996
@@ -0,0 +1,52 @@
+/*
+ *	scsi_module.c Copyright (1994, 1995) Eric Youngdale.
+ *
+ * Support for loading low-level scsi drivers using the linux kernel loadable
+ * module interface.
+ *
+ * To use, the host adapter should first define and initialize the variable
+ * driver_template (datatype Scsi_Host_Template), and then include this file.
+ * This should also be wrapped in a #ifdef MODULE/#endif.
+ *
+ * The low -level driver must also define a release function which will
+ * free any irq assignments, release any dma channels, release any I/O
+ * address space that might be reserved, and otherwise clean up after itself.
+ * The idea is that the same driver should be able to be reloaded without
+ * any difficulty.  This makes debugging new drivers easier, as you should
+ * be able to load the driver, test it, unload, modify and reload.
+ *
+ * One *very* important caveat.  If the driver may need to do DMA on the
+ * ISA bus, you must have unchecked_isa_dma set in the device template,
+ * even if this might be changed during the detect routine.  This is
+ * because the shpnt structure will be allocated in a special way so that
+ * it will be below the appropriate DMA limit - thus if your driver uses
+ * the hostdata field of shpnt, and the board must be able to access this
+ * via DMA, the shpnt structure must be in a DMA accessible region of
+ * memory.  This comment would be relevant for something like the buslogic
+ * driver where there are many boards, only some of which do DMA onto the
+ * ISA bus.  There is no convenient way of specifying whether the host
+ * needs to be in a ISA DMA accessible region of memory when you call
+ * scsi_register.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+char kernel_version[] = UTS_RELEASE;
+
+int init_module(void) {
+	driver_template.usage_count = &mod_use_count_;
+	scsi_register_module(MODULE_SCSI_HA, &driver_template);
+	return (driver_template.present == 0);
+}
+
+void cleanup_module( void) {
+	if (MOD_IN_USE) {
+		printk(KERN_INFO __FILE__ ": module is in use, remove rejected\n");
+	      }
+#ifdef SCSI_CLEANUP
+	SCSI_CLEANUP;
+#endif
+	scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+}
+
diff -urNwbB linux/arch/arm/drivers/sound/Makefile linux.arm/arch/arm/drivers/sound/Makefile
--- linux/arch/arm/drivers/sound/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/Makefile	Sun Feb 11 09:32:34 1996
@@ -0,0 +1,13 @@
+
+L_TARGET := sound.a
+L_OBJS	 :=sndvars.o soundcont.o soundinit.o soundirq.o voice.o \
+	 voice_samp.o voice_wave.o v_drv_wave.o v_drv_sam.o
+M_OBJS   :=
+
+L_OBJS	+= audio.o
+L_OBJS	+= arcaudio.o
+L_OBJS	+= mkbeep.o
+L_OBJS	+= debug.o
+
+include $(TOPDIR)/Rules.make
+
diff -urNwbB linux/arch/arm/drivers/sound/arcaudio.c linux.arm/arch/arm/drivers/sound/arcaudio.c
--- linux/arch/arm/drivers/sound/arcaudio.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/arcaudio.c	Sun Mar  3 12:51:13 1996
@@ -0,0 +1,235 @@
+/* 
+ * linux/arch/arm/drivers/sound/arcaudio.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * ARC audio driver
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+
+#include <asm/arcaudio.h>
+
+#include "soundinit.h"
+#include "soundcont.h"
+#include "soundirq.h"
+#include "voice.h"
+
+#include "asm/segment.h"
+
+static int spkr;
+extern unsigned char *ioc;
+
+static int arcaudio_write(struct inode *inode, struct file *filp, const char *buf, int nr)
+{
+	int retval=0,c;
+	struct voice_driver *vd=(struct voice_driver *)filp->private_data;
+	const char *b = buf;
+	struct wait_queue wait;
+
+	wait.task = current;
+	wait.next = NULL;
+
+	add_wait_queue(&vd->vd_wait, &wait);
+	while(1) {
+		current->state = TASK_INTERRUPTIBLE;
+		if(current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+
+		c = vd->vd_write(vd,b,nr);
+		b += c;
+		nr -= c;
+		if(nr<=0)
+			break;
+
+		if (filp->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&vd->vd_wait, &wait);
+	return (b - buf) ? b - buf : retval;
+}
+
+static int arcaudio_open(struct inode *inode, struct file *filp)
+{
+	int voice;
+	int channel;
+	struct voice_driver *driver;
+	
+	channel = voice_findfree(0);
+	
+	if(channel < 0)
+		return /* channel */-EBUSY;
+	
+	if((voice = voice_allocate(VOICE_SAMPLE, 0))<0)
+		return voice;
+	
+	driver = (struct voice_driver *)voice;
+	
+	driver->vd_convtable = table_linsigned;
+	driver->vd_bits      = 16;
+	filp->private_data   = (void *)driver;
+	voice_install(driver,channel);
+	
+	return 0;
+}
+
+static void arcaudio_release(struct inode *inode, struct file *filp)
+{
+	voice_release((struct voice_driver *)filp->private_data);
+}
+
+static int
+arcaudio_getinfo(struct arcaudio *ainfo)
+{
+	int sr, nc;
+	sound_configure(0,0,&sr,&nc);
+	put_fs_long(sr, &ainfo->sample_rate);
+	put_fs_long(nc, &ainfo->num_channels);
+	put_fs_long(sound_vol(-1), &ainfo->volume);
+	return 0;
+}
+
+static int
+arcaudio_setinfo(struct arcaudio *ainfo)
+{
+	int ret;
+  
+	if((ret=sound_configure(get_fs_long(&ainfo->sample_rate), 
+				get_fs_long(&ainfo->num_channels), NULL, NULL)) < 0)
+		return ret;
+	if((ret=sound_vol(get_fs_long(ainfo->volume))) < 0)
+		return ret;
+	return 0;
+}
+
+static int
+arcaudio_getchannelinfo(struct voice_driver *vd, struct arcaudio_channel *ainfo)
+{
+	enum ch_type t;
+	put_fs_long((sound_stereoimage[vd->vd_channel]-SOUND_CENTRE)<<24,
+					&ainfo->stereo_position);
+	put_fs_long(vd->vd_volume,&ainfo->channel_volume);
+	switch(vd->vd_bits)
+	{
+		case 0: t = ARCAUDIO_NONE;          break;
+		case 8: t = ARCAUDIO_8BITSIGNED;    break;
+		case 9: t = ARCAUDIO_8BITUNSIGNED;  break;
+		case 16:t = ARCAUDIO_16BITSIGNED;   break;
+		case 17:t = ARCAUDIO_16BITUNSIGNED; break;
+		default:t = ARCAUDIO_LOG;           break;
+	}
+	put_fs_long(t, &ainfo->channel_type);
+	put_fs_long(vd->vd_size, &ainfo->buffer_size);
+	return 0;
+}
+
+static int
+arcaudio_setchannelinfo(struct voice_driver *vd, struct arcaudio_channel *ainfo)
+{
+	int newimage, newvol, newbits /* , newbuff */;
+	
+	newimage = ((get_fs_long(&ainfo->stereo_position)>>24) + SOUND_CENTRE)&255;
+	if(newimage<0 || newimage>255)
+		return -EINVAL;
+	newvol = get_fs_long(&ainfo->channel_volume);
+	if(newvol<0 || newvol>127)
+		return -EINVAL;
+	switch(get_fs_long(&ainfo->channel_type))
+	{
+		case ARCAUDIO_NONE:		newbits = 0;	break;
+		case ARCAUDIO_8BITSIGNED:	newbits = 8;	break;
+		case ARCAUDIO_8BITUNSIGNED:	newbits = 9;	break;
+		case ARCAUDIO_16BITSIGNED:	newbits = 16;	break;
+		case ARCAUDIO_16BITUNSIGNED:	newbits = 17;	break;
+		case ARCAUDIO_LOG:		newbits = 1;	break;
+		default:
+			return -EINVAL;
+	}
+	
+	sound_setimage(vd->vd_channel, newimage);
+	vd->vd_bits = newbits;
+	voice_volume(vd, newvol);
+	return 0;
+}
+
+static int
+arcaudio_getopts(unsigned long *arg)
+{
+	put_fs_long(spkr ? 1 : 0, arg);
+	return 0;
+}
+
+static int
+arcaudio_setopts(unsigned long *arg)
+{
+	spkr = get_fs_long(arg) & 1;
+	if(spkr)
+		ioc[0] = (ioc[0] & ~0x20) | 0xc0;
+	else
+		ioc[0] = (ioc[0] | 0x20) | 0xc0;
+	return 0;
+}
+
+static int
+arcaudio_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	switch(cmd)
+	{
+		case ARCAUDIO_GETINFO:
+			return arcaudio_getinfo((struct arcaudio *)arg);
+		
+		case ARCAUDIO_SETINFO:
+			return arcaudio_setinfo((struct arcaudio *)arg);
+		
+		case ARCAUDIO_GETCHANNELINFO:
+			return arcaudio_getchannelinfo((struct voice_driver *)
+					filp->private_data,(struct arcaudio_channel *)arg);
+		
+		case ARCAUDIO_SETCHANNELINFO:
+			return arcaudio_setchannelinfo((struct voice_driver *)
+					filp->private_data,(struct arcaudio_channel *)arg);
+
+		case ARCAUDIO_GETOPTS:
+			return arcaudio_getopts((unsigned long *)arg);
+		case ARCAUDIO_SETOPTS:
+			return arcaudio_setopts((unsigned long *)arg);
+		default:
+		  return -EINVAL;
+	}
+}
+
+static struct file_operations arcaudio_fops =
+{
+/* 	arcaudio_lseek, */NULL,
+/* 	arcaudio_read, */NULL,
+	arcaudio_write,
+	NULL,
+/* 	arcaudio_select, */NULL,
+	arcaudio_ioctl,
+	NULL,
+	arcaudio_open,
+	arcaudio_release,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+void arcaudio_init(void)
+{
+  if(register_sounddev(16, "arcaudio", &arcaudio_fops, NULL))
+    printk("\nSound: can't register arc audio device\n");
+  else
+    printk("arcaudio ");
+
+  spkr = (ioc[0] & 0x20) == 0;
+}
diff -urNwbB linux/arch/arm/drivers/sound/audio.c linux.arm/arch/arm/drivers/sound/audio.c
--- linux/arch/arm/drivers/sound/audio.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/audio.c	Sun Feb 11 09:32:35 1996
@@ -0,0 +1,139 @@
+/*
+ * audio.c
+ *
+ * Sun /dev/audio driver
+ *
+ */
+
+/* #include <linux/types.h> */
+/* #include <linux/interrupt.h> */
+/* #include <linux/kernel.h> */
+/* #include <linux/major.h> */
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include "soundinit.h"
+#include "soundcont.h"
+#include "voice.h"
+
+#include <asm/segment.h>
+
+static struct wait_queue *open_wait=NULL;
+static int use=0;
+static int waiting=0;
+
+static int audio_write(struct inode *inode, struct file *filp, const char *buf, int nr)
+{
+	int retval=0,c;
+	struct voice_driver *vd=(struct voice_driver *)filp->private_data;
+	const char *b = buf;
+	struct wait_queue wait;
+
+	wait.task = current;
+	wait.next = NULL;
+
+	add_wait_queue(&vd->vd_wait, &wait);
+	while(1) {
+		current->state = TASK_INTERRUPTIBLE;
+		if(current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+
+		c = vd->vd_write(vd,b,nr);
+		b += c;
+		nr -= c;
+		if(nr<=0)
+			break;
+
+		if (filp->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&vd->vd_wait, &wait);
+	return (b - buf) ? b - buf : retval;
+}
+
+static int
+audio_open(struct inode *inode, struct file *filp)
+{
+	int voice;
+	struct voice_driver *driver;
+	struct wait_queue wait;
+	wait.task = current;
+	wait.next = NULL;
+
+	use++;
+	if(use!=1)
+	{
+		use--;
+		if(filp->f_flags & O_NONBLOCK)
+			return -EBUSY;
+		add_wait_queue(&open_wait, &wait);
+		waiting++;
+		while(use)
+		{
+			current->state = TASK_INTERRUPTIBLE;
+			schedule();
+		}
+		waiting--;
+		remove_wait_queue(&open_wait, &wait);
+		use++;
+	}
+
+	if((voice=voice_allocate(VOICE_SAMPLE,0))<0)
+		return voice;
+
+	driver = (struct voice_driver *)voice;
+
+	driver->vd_convtable	=table_linsigned;
+	driver->vd_bits		=8;			/* signed 8 bit */
+	filp->private_data=(void *)driver;
+	voice_install(driver,0);
+	return 0;
+}
+
+static void
+audio_release(struct inode *inode, struct file *filp)
+{
+	voice_release((struct voice_driver *)filp->private_data);
+	use--;
+	if(waiting)
+		wake_up_interruptible(&open_wait);
+}
+
+static int
+audio_ioctl(struct inode * inode, struct file * file,
+			     unsigned int cmd, unsigned long arg)
+{/* Not yet implemented */
+	return -EINVAL;
+}
+
+static struct file_operations audio_fops =
+{
+/* 	audio_lseek, */NULL,
+/* 	audio_read, */NULL,
+	audio_write,
+	NULL,
+/* 	audio_select, */NULL,
+	audio_ioctl,
+	NULL,
+	audio_open,
+	audio_release,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+void audio_init(void)
+{
+  if(register_sounddev(4,"sunaudio", &audio_fops, NULL))
+    printk("Sound: can't register sun audio device\n");
+  else
+    printk("sunaudio ");
+}
diff -urNwbB linux/arch/arm/drivers/sound/config.h linux.arm/arch/arm/drivers/sound/config.h
--- linux/arch/arm/drivers/sound/config.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/config.h	Sun Feb 11 09:32:35 1996
@@ -0,0 +1,43 @@
+/*
+ * config.h
+ *
+ * Sound system configuration file
+ *
+ */
+
+#ifndef __sound_config_h
+#define __sound_config_h
+
+/*
+ * This is the number of channels that the system has
+ * *** Hardcoded in soundirq.s
+ */
+#define N_CHANNELS	8
+
+/*
+ * This is the default number of channels initially scanned
+ */
+#define SOUND_DEFAULTLGCHANNELS	0
+
+/*
+ * This is the default buffer length.  MUST be a multiple of 16
+ */
+#define SOUND_DEFAULTLENGTH	208
+
+/*
+ * This is the default period between samples output between channels (72)
+ */
+#define SOUND_DEFAULTPERIOD	72
+
+/*
+ * This is the output buffer size.
+ * *** Hardcoded in soundvars.s
+ */
+#define SOUND_SNDBUFSIZE	0x1000
+
+/*
+ * Max number of /dev devices
+ */
+#define MAX_OPS 20
+
+#endif
diff -urNwbB linux/arch/arm/drivers/sound/debug.c linux.arm/arch/arm/drivers/sound/debug.c
--- linux/arch/arm/drivers/sound/debug.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/debug.c	Sun Mar  3 12:51:38 1996
@@ -0,0 +1,68 @@
+/*
+ * linux/arch/arm/drivers/sound/debug.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Show debugging information
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+
+#include "soundinit.h"
+#include "soundirq.h"
+#include "voice.h"
+
+static int
+debug_read(struct inode *inode, struct file *filp, char *buf, int nr)
+{
+	int i,j;
+	int s=filp->f_pos;
+
+	i=sprintf(buf,"Sound status:\n\tChannels: %d\n\tPeriod  : %d us\n\t"
+		"Volume : %d\n\tBuf len: %d\n\nChannels:\n",
+		1<<sound_lognchannels,sound_sampleperiod & 0xff,
+		sound_volume,sound_samplelength);
+	for(j=0;j<8;j++)
+		i+=sprintf(buf+i,"\t0: %s\n",
+			sound_voice[j]==&sound_quiet_voice?"inuse":"unused");
+	memmove(buf,buf+s,i-s);
+	return i-s;
+}
+
+static int
+debug_open(struct inode *inode, struct file *filp)
+{
+	return 0;
+}
+
+static void
+debug_release(struct inode *inode, struct file *filp)
+{
+}
+
+static struct file_operations debug_fops =
+{
+	NULL,
+	debug_read,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	debug_open,
+	debug_release,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+void debug_init(void)
+{
+	if(register_sounddev(6,"debug", &debug_fops, NULL))
+		printk("Sound: can't register debug device\n");
+}
diff -urNwbB linux/arch/arm/drivers/sound/mkbeep.c linux.arm/arch/arm/drivers/sound/mkbeep.c
--- linux/arch/arm/drivers/sound/mkbeep.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/mkbeep.c	Sun Mar  3 12:52:05 1996
@@ -0,0 +1,39 @@
+/*
+ * linux/arch/arm/drivers/sound/mkbeep.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include "soundinit.h"
+#include "soundirq.h"
+#include "voice.h"
+
+/* Make a beep of frequency 'freq' HZ and 'duration' cs */
+
+int mk_beep(int freq, int vol, int duration)
+{
+	int voice;
+	struct voice_driver *driver;
+
+	if (duration == 0)
+		return 0;
+
+	if (sound_voice[0] != &sound_quiet_voice)
+		return 0;
+
+	if ((voice = voice_allocate(VOICE_WAVE, 0)) < 0)
+		return voice;
+
+	driver = (struct voice_driver *)voice;
+
+	driver->vd_dur    = duration * 20833 / 100;
+	driver->vd_phase  = freq;
+	driver->vd_volume = vol;
+
+	voice_install (driver, 0);
+	return 0;
+}
diff -urNwbB linux/arch/arm/drivers/sound/sndvars.S linux.arm/arch/arm/drivers/sound/sndvars.S
--- linux/arch/arm/drivers/sound/sndvars.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/sndvars.S	Sun Mar  3 13:01:52 1996
@@ -0,0 +1,64 @@
+/*
+ * linux/arch/arm/drivers/sound/sndvars.S
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Absolute variable locations in the sound system
+ *
+ * Map is:
+ *   0x0800		=	sound buffer address
+ *   0x0804		=	sound system update flags
+ *   0x0808		=	log of number of channels available
+ *   0x080C		=	sample length
+ *   0x0810		=	Vidc sample period
+ *   0x0814		=	Stereo Image registers
+ *   0x0834		=	Voice registers
+ *   0x0854		=	Volume
+ *   0x1000 - 0x1FFF	=	sound buffer 0
+ *   0x2000 - 0x2FFF	=	sound buffer 1
+ *   0x4100		=	Signed linear conversion table
+ *   0x4200		=	Logarithmic conversion table
+ */
+
+#define N_CHANNELS	8
+
+			.global	_sound_datastart
+			.global	_sound_buffer
+			.global	_sound_lognchannels
+			.global	_sound_samplelength
+			.global	_sound_vidcsampleperiod
+			.global	_sound_toupdate
+			.global	_sound_stereoimage
+			.global	_sound_voice
+			.global	_sound_buffer0
+			.global	_sound_buffer1
+			.global	_sound_volume
+			.global	_table_linsigned
+			.global	_table_log
+
+			.org	0
+Ls:	
+			.org	0x800
+.equ _sound_datastart, . - Ls
+.equ _sound_buffer, . - Ls
+			.space	4		@3
+.equ _sound_toupdate, . - Ls
+			.space	4		@4
+.equ _sound_lognchannels, . - Ls
+			.space	4		@5
+.equ _sound_samplelength, . - Ls
+			.space	4		@6
+.equ _sound_vidcsampleperiod, . - Ls
+			.space	4	@7
+.equ _sound_stereoimage, . - Ls
+			.space	4*N_CHANNELS
+.equ _sound_voice, . - Ls
+			.space	4*N_CHANNELS
+.equ _sound_volume, . - Ls
+			.space	4
+
+.equ _sound_buffer0, 0x1000
+.equ _sound_buffer1, 0x2000
+
+.equ _table_linsigned, 0x4100
+.equ _table_log, 0x4200
diff -urNwbB linux/arch/arm/drivers/sound/soundcont.c linux.arm/arch/arm/drivers/sound/soundcont.c
--- linux/arch/arm/drivers/sound/soundcont.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundcont.c	Sun Mar  3 12:52:27 1996
@@ -0,0 +1,150 @@
+/*
+ * linux/arch/arm/drivers/sound/soundcont.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Control various sound parameters.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include "soundinit.h"
+#include "soundcont.h"
+
+#include "asm/system.h"
+
+#define MAX(x,y)	(x) < (y) ? (y) : (x)
+
+int sound_sampleperiod;			/* our *real* idea of no. of us between sample o/ps */
+unsigned char sound_allocated[N_CHANNELS];
+#define VIDC_SAMPLERATE(x) (((x)*3)>>1)
+
+/*
+ * Set the sample period
+ */
+
+int sound_configure(int period, int nchannels, int *oldperiod, int *oldnchannels)
+{
+	int snew=0, vnew=0, cnew=sound_lognchannels;
+	unsigned long flags;
+	
+	if(oldperiod)
+		*oldperiod = sound_sampleperiod << sound_lognchannels;
+	if(oldnchannels)
+		*oldnchannels = 1<<sound_lognchannels;
+	
+	if(nchannels != 0)
+	{
+		switch(nchannels)
+		{
+			case 1: cnew = 0; break;
+			case 2: cnew = 1; break;
+			case 3:
+			case 4: cnew = 2; break;
+			case 5:
+			case 6:
+			case 7:
+			case 8: cnew = 3; break;
+			default: return -EINVAL;
+		}
+		if(period == 0)
+			period = sound_sampleperiod << sound_lognchannels;
+	}
+	
+	save_flags(flags);
+	
+	if(period != 0)
+	{
+		snew=period>>cnew;
+		vnew=VIDC_SAMPLERATE(snew);
+		if(vnew < 3 || vnew > 255)
+			return -EINVAL;
+
+		cli();
+		sound_sampleperiod = snew;
+		sound_vidcsampleperiod = vnew|0x100;
+		sound_toupdate|=SOUND_SNDFREQ;
+	}
+	
+	if(cnew != sound_lognchannels)
+		sound_lognchannels = cnew;
+	restore_flags(flags);
+	
+	return 0;
+}
+
+/*
+ * Set the overall volume of the system.
+ * This only works if the voice generator is using the tables
+ */
+int sound_vol(int volume)
+{
+	int i,thislen,len,sample,p,n;
+	int old;
+	unsigned char *bp,*ep;
+
+	if(volume==-1)
+		return sound_volume;
+
+	if(volume<0 || volume>127)
+		return -EINVAL;
+
+	old=sound_volume;
+
+	sound_volume=volume;
+
+	volume = (127-(volume & 127))<<1;
+
+	for(i = 0; i < 256; i++)
+		table_log[i] = MAX(i-volume, 0);
+
+	bp = table_linsigned;
+	ep = bp+8192;
+
+	*bp++ = 0;
+
+	len = 1;
+	sample = 0;
+
+	while(1)
+	{
+		n=15;
+		do
+		{
+			thislen = len;
+			sample+=2;
+			if(sample>254)
+				sample=254;
+
+			p=MAX(sample-volume,0);
+			/* Insert samples to cover this minor block */
+			do
+			{
+				*bp++ = p;
+				*--ep = p | 1;
+				if(bp >= ep)
+					goto end;
+			}
+			while(--thislen > 0);
+		}
+		while(--n>=0);
+		len <<= 1;
+	}
+end:
+	return old;
+}
+
+int sound_setimage(int channel, int image)
+{
+	int i,n=1 << sound_lognchannels;
+
+	printk("Setting channel %d (inc %d):",channel, n);
+	for(i=channel; i<N_CHANNELS; i+=n)
+	{
+		sound_stereoimage[i] = image;
+	}
+	printk("\n");
+	sound_toupdate |= SOUND_IMAGE;
+	return 0;
+}
diff -urNwbB linux/arch/arm/drivers/sound/soundcont.h linux.arm/arch/arm/drivers/sound/soundcont.h
--- linux/arch/arm/drivers/sound/soundcont.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundcont.h	Sun Feb 11 09:32:35 1996
@@ -0,0 +1,39 @@
+/*
+ * soundcont.h
+ *
+ * Control various sound parameters
+ *
+ */
+
+#ifndef __sound_soundcont_h
+#define __sound_soundcont_h
+
+/*
+ * Two tables used in voice generators.
+ * These are scaled according to the current overall volume setting
+ */
+
+extern unsigned char table_linsigned[];
+extern unsigned char table_log[];
+extern unsigned char sound_allocated[];
+
+/*
+ * Change the sample period of the system and number of active channels.
+ */
+extern int sound_configure(int period, int channels, int *operiod, int *ochannels);
+
+/*
+ * Set the overall volume of the system.
+ */
+extern int sound_vol(int volume);
+
+/* 
+ * Set the stereo location of this channel
+ */
+extern int sound_setimage(int channel, int image);
+
+/*
+ * Increment the sound usage counter and enable sound irqs
+ */
+extern void soundirq_inc(void);
+#endif
diff -urNwbB linux/arch/arm/drivers/sound/soundinit.c linux.arm/arch/arm/drivers/sound/soundinit.c
--- linux/arch/arm/drivers/sound/soundinit.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundinit.c	Sun Mar  3 12:52:51 1996
@@ -0,0 +1,137 @@
+/*
+ * linux/arch/arm/drivers/sound/soundinit.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Initialise the sound system
+ *
+ * * Fill buffers with zero
+ * * Set up stereo image registers
+ * * Set up Sstart and SendN.
+ * * Mark SendC for update
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+
+#include <asm/irq-no.h>
+#include <asm/irq.h>
+
+#include "soundinit.h"
+#include "soundirq.h"
+#include "soundcont.h"
+
+extern unsigned char sound_buffer0[],sound_buffer1[];
+extern int sound_vidcsampleperiod, sound_lognchannels;
+extern int sound_buffer;
+extern void debug_init(void);
+extern void audio_init(void);
+extern void arcaudio_init(void);
+
+struct sound_ops snddevs[MAX_OPS];
+
+int sound_setup(void)
+{
+	return 0;
+}
+
+int
+register_sounddev(unsigned int minor, const char *name,
+	struct file_operations *fops, void *data)
+{
+	if(minor >= MAX_OPS)
+		return -EINVAL;
+	if(snddevs[minor].name)
+		return -EBUSY;
+	snddevs[minor].name = name;
+	snddevs[minor].fops = fops;
+	snddevs[minor].data = data;
+	return 0;
+}
+
+int
+unregister_sounddev(unsigned int minor, const char *name)
+{
+	if(minor >= MAX_OPS || !snddevs[minor].name || strcmp(snddevs[minor].name,name))
+		return -EINVAL;
+	snddevs[minor].name = NULL;
+	snddevs[minor].fops = NULL;
+	return 0;
+}
+
+static int
+sound_open(struct inode *inode, struct file *filp)
+{
+	int dev = MINOR(inode->i_rdev);
+	if(dev >= MAX_OPS || !snddevs[dev].name)
+		return -ENODEV;
+	filp->f_op = snddevs[dev].fops;
+	if(filp->f_op->open)
+		return filp->f_op->open(inode,filp);
+	return 0;
+}
+
+static struct file_operations sound_fops =
+{
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	sound_open,
+	NULL,
+};
+
+void sound_init(void)
+{
+	int i;
+
+	printk("Sound: Development sound version 0.01\n");
+
+	sound_toupdate=0;
+
+	for(i=0; i<SOUND_SNDBUFSIZE; i++)
+		sound_buffer0[i]=sound_buffer1[i]=0;
+
+	for(i=0; i<N_CHANNELS; i++)
+	{
+		sound_stereoimage[i]=SOUND_CENTRE;
+		sound_voice[i]=&sound_quiet_voice;
+		sound_allocated[i]=0;
+	}
+
+	/* RiscOS defaults */
+
+	if(sound_configure(48, /* SOUND_DEFAULTCHANNELS */1, NULL, NULL)<0)
+	  printk("sound_configure: error\n");
+
+	sound_samplelength=SOUND_DEFAULTLENGTH;
+	sound_buffer=0;
+	sound_toupdate|=SOUND_SNDFREQ|SOUND_IMAGE;
+
+	sound_volume=-1;
+	sound_vol(127);
+
+	if(request_irq(IRQ_SOUNDCHANGE, sound_irq, SA_INTERRUPT, "sound"))
+	{
+	  printk("Sound: could not claim irq %d\n",IRQ_SOUNDCHANGE);
+	  return;
+	}
+
+	register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
+
+	printk("Sound: ");
+	debug_init();
+	audio_init();
+	arcaudio_init();
+	printk("\n");
+}
diff -urNwbB linux/arch/arm/drivers/sound/soundinit.h linux.arm/arch/arm/drivers/sound/soundinit.h
--- linux/arch/arm/drivers/sound/soundinit.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundinit.h	Sun Feb 11 09:32:35 1996
@@ -0,0 +1,70 @@
+/*
+ * soundinit.h
+ *
+ * These are the structures used in initialising and maintaining the
+ * sound system.
+ */
+
+#ifndef __sound_soundinit_h
+#define __sound_soundinit_h
+
+#ifndef __sound_config_h
+#include "config.h"
+#endif
+
+/*
+ * This structure contains the info for a /dev device.
+ */
+struct sound_ops
+{
+  const char *name;
+  struct file_operations *fops;
+  void *data;
+};
+
+/*
+ * This is the log of the current number of channels currently configured.
+ */
+extern int sound_lognchannels;
+
+/*
+ * This is the sample length for one sample in the output buffer
+ */
+extern int sound_samplelength;
+
+/*
+ * This is the output period. (Multiply by no. of channels for sample period)
+ */
+extern int sound_sampleperiod;
+
+extern int sound_vidcsampleperiod;
+
+/*
+ * This is the current overall volume of the sound system.
+ */
+extern int sound_volume;
+
+/*
+ * This is to tell the interrupt routine to reprogram certain registers
+ */
+extern int sound_toupdate;
+#define SOUND_SNDFREQ		(1<<0)
+#define SOUND_IMAGE		(1<<1)
+
+/*
+ * This contains the current stereo positions of each 'output' channel.
+ */
+extern int sound_stereoimage[];
+#define SOUND_LEFT		32
+#define SOUND_83LEFT		64
+#define SOUND_67LEFT		96
+#define SOUND_CENTRE		128
+#define SOUND_67RIGHT		160
+#define SOUND_83RIGHT		192
+#define SOUND_RIGHT		224
+
+extern int register_sounddev(unsigned int minor, const char *name,
+	struct file_operations *fops, void *data);
+extern int unregister_sounddev(unsigned int minor, const char *name);
+
+#endif
diff -urNwbB linux/arch/arm/drivers/sound/soundirq.S linux.arm/arch/arm/drivers/sound/soundirq.S
--- linux/arch/arm/drivers/sound/soundirq.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundirq.S	Sun Mar  3 12:53:32 1996
@@ -0,0 +1,255 @@
+/*
+ * linux/arch/arm/drivers/sound/soundirq.s
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Sound interrupt handling
+ */
+
+		.text
+
+		.global	_sound_irq
+
+#define vidc	0x03400000
+#define memc	0x03600000
+
+LCsound_datastart:
+		.word	_sound_datastart
+LCsoundirq_use:
+		.word	_soundirq_use
+LCmemc_ctrl_reg:
+		.word	_memc_ctrl_reg
+
+_sound_irq:	stmfd	sp!, {r4-r12,lr}
+		ldr	r12, [pc, #LCsound_datastart - . - 8]
+		mov	r9, #vidc
+		ldmia	r12, {r3-r7}
+		tst	r4, #1			@ Update sound period?
+		andne	r7, r7, #0x000000ff
+		orrne	r7, r7, #0x00000100
+		orrne	r7, r7, #0xC0000000
+		strne	r7, [r9]
+		tst	r4, #2			@ Update image registers?
+		beq	_noupdate_image
+		add	r0, r12, #20+8*4
+		mov	r2, #0x80000000
+LC00:		sub	r2, r2, #0x04000000
+		ldr	r1, [r0, #-4]!
+		and	r1, r1, #0xff
+		orr	r1, r2, r1, lsr #5
+		bic	r1, r1, #0x83000000
+		str	r1, [r9]
+		tst	r2, #0x1C000000
+		bne	LC00
+_noupdate_image:
+		BIC	r4,r4,#3
+		EORS	r3,r3,#1
+		STMIA	r12,{r3,r4}
+		MOVEQ	r0,#0x1000		@ Buffer start
+		MOVNE	r0,#0x2000
+		ADD	r1,r0,r6, lsl  r5		@ Buffer end
+		MOV	r2,#1
+		MOV	r2,r2, lsl  r5
+		STMFD	sp!,{r0-r2,r12}
+		TEQ	r5,#2
+		BEQ	_chan_4
+		TEQ	r5,#1
+		BEQ	_chan_2
+		TEQ	r5,#0
+		BEQ	_chan_1
+		ADD	r0,r0,#7
+		ADD	r1,r1,#7
+		ADD	r12,r12,#80
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call eighth voice generator
+		LDMIA	sp,{r0-r2,r12}
+		ADD	r0,r0,#6
+		ADD	r1,r1,#6
+		ADD	r12,r12,#76
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call seventh voice generator
+		LDMIA	sp,{r0-r2,r12}
+		ADD	r0,r0,#5
+		ADD	r1,r1,#5
+		ADD	r12,r12,#72
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call sixth generator
+		LDMIA	sp,{r0-r2,r12}
+		ADD	r0,r0,#4
+		ADD	r1,r1,#4
+		ADD	r12,r12,#68
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call fifth generator
+		LDMIA	sp,{r0-r2,r12}
+_chan_4:	ADD	r0,r0,#3
+		ADD	r1,r1,#3
+		ADD	r12,r12,#64
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call fourth generator
+		LDMIA	sp,{r0-r2,r12}
+		ADD	r0,r0,#2
+		ADD	r1,r1,#2
+		ADD	r12,r12,#60
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call third generator
+		LDMIA	sp,{r0-r2,r12}
+_chan_2:	ADD	r0,r0,#1
+		ADD	r1,r1,#1
+		ADD	r12,r12,#56
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call second generator
+		LDMIA	sp,{r0-r2,r12}
+_chan_1:	ADD	r12,r12,#52
+		LDR	r3,[r12]
+		MOV	lr,pc
+		LDR	pc,[r3]			@ Call first generator
+		LDMIA	sp!,{r0-r2,r12}
+		mov	r3, #memc | 0x00080000
+		orr	r3, r3, #0x0001e000
+		sub	r1, r1, #16
+		orr	r2, r3, r1, lsr #2
+		orr	r2, r2, #0x00020000
+		strb	r2, [r2]		@ Set up SendN
+		orr	r2, r3, r0, lsr #2
+		strb	r2, [r2]		@ Set up Sstart
+		ldr	r4, [pc, #LCsoundirq_use - . - 8]
+		ldr	r4, [r4]
+		teq	r4, #0
+		ldmnefd	sp!, {r4-r12,pc}^
+		ldr	r0, [pc, #LCmemc_ctrl_reg - . - 8]
+		LDR	r1, [r0]
+		bic	r1, r1, #0xFF000000
+		bic	r1, r1, #0x00FF0000
+		bic	r1, r1, #0x0000E000 | 1<<11
+		str	r1, [r0]
+		orr	r1, r1, #memc
+		orr	r1, r1, #0x000E0000
+		str	r1, [r1]
+		ldmfd	sp!, {r4-r12,pc}^
+
+		.global	_soundirq_dec
+_soundirq_dec:	ldr	r0, [pc, #LCsoundirq_use - . - 8]
+		ldr	r1, [r0]
+		subs	r1, r1, #1
+		movmi	r1, #0
+		str	r1, [r0]
+		movs	pc, lr
+
+		.global	_soundirq_inc
+_soundirq_inc:	stmfd	sp!, {lr}
+		ldr	r0, [pc, #LCsoundirq_use - . - 8]
+		ldr	r1, [r0]
+		add	r1, r1, #1
+		str	r1, [r0]
+		teq	r1, #1
+		movnes	pc, lr
+		bl	_sound_irq		@ causes first fill & sets up
+		mov	r1, #memc | 0x000C0000
+		str	r1, [r1]			@ force swap
+		ldr	r0, [pc, #LCmemc_ctrl_reg - . - 8]
+		ldr	r1, [r0]
+		bic	r1, r1, #0xFF000000
+		bic	r1, r1, #0x00FF0000
+		bic	r1, r1, #0x0000E000
+		orr	r1, r1, #1<<11
+		str	r1, [r0]
+		orr	r1, r1, #memc
+		orr	r1, r1, #0x000E0000
+		str	r1, [r1]
+		ldmfd	sp!, {pc}^
+
+		.global	_sound_quiet_voice
+_sound_quiet_voice:
+		.word	_sound_quiet
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+
+		.global	_sound_quiet
+_sound_quiet:	MOV	r3,#0
+LC01:		STRB	r3,[r0],r2
+		EOR	r3,r3,#1
+		CMP	r0,r1
+		BLT	LC01
+		MOVS	pc,lr
+
+		.global	_sound_to_quiet_voice
+_sound_to_quiet_voice:
+		.word	_sound_to_quiet
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+
+_sound_to_quiet:
+		ADR	r4,_sound_to_quiet_voice2
+		STR	r4,[r12]
+		MOV	r3,#0
+LC02:		STRB	r3,[r0],r2
+		EOR	r3,r3,#1
+		CMP	r0,r1
+		BLT	LC02
+		MOVS	pc,lr
+
+_sound_to_quiet_voice2:
+		.word	_sound_to_quiet2
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+		.word	0
+
+_sound_to_quiet2:
+		ADR	r4,_sound_quiet_voice
+		STR	r4,[r12]
+		MOV	r3,#0
+LC03:		STRB	r3,[r0],r2
+		EOR	r3,r3,#1
+		CMP	r0,r1
+		BLT	LC03
+		B	_soundirq_dec
+
+		.data
+
+_soundirq_use:	.word	0
+
diff -urNwbB linux/arch/arm/drivers/sound/soundirq.h linux.arm/arch/arm/drivers/sound/soundirq.h
--- linux/arch/arm/drivers/sound/soundirq.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundirq.h	Sun Feb 11 09:32:36 1996
@@ -0,0 +1,48 @@
+/*
+ * soundirq.h
+ *
+ * Handle sound system interrupts
+ *
+ */
+
+#ifndef __sound_soundirq_h
+#define __sound_soundirq_h
+
+#ifndef __sound_config_h
+#include "config.h"
+#endif
+
+/*
+ * Process the current sound interrupt, and swap internal buffers
+ */
+extern void sound_irq(int irq_nr, struct pt_regs *regs);
+
+/*
+ * Data structure used by interrupt routine to call the low-level
+ * code in the voice generators to re-fill the output buffer.
+ */
+struct voice
+{
+  /* The fill routine */
+  void (*fill)(char *bufstart,char *bufend,char increment);
+  void (*gateon)(struct voice *);
+  void (*gateoff)(struct voice *);
+  /* Other data */
+  int  data[8];
+};
+
+/*
+ * This is a quiet voice generator
+ *
+ * This is used when a voice generator has been removed from a
+ * channel.
+ */
+extern struct voice sound_quiet_voice;
+extern struct voice sound_to_quiet_voice;
+
+/*
+ * This is the data used by the interrupt routine to call the voice generators
+ */
+extern struct voice *sound_voice[];
+
+#endif
diff -urNwbB linux/arch/arm/drivers/sound/v_drv_sam.S linux.arm/arch/arm/drivers/sound/v_drv_sam.S
--- linux/arch/arm/drivers/sound/v_drv_sam.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/v_drv_sam.S	Sun Mar  3 12:57:04 1996
@@ -0,0 +1,103 @@
+/*
+ * linux/arch/arm/drivers/sound/v_drv_sam.S
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Voice low level filler.
+ *
+ * Takes data out of the voice buffer, translates it,
+ * and puts it in the output buffer.
+ */
+
+		.text
+
+		.global	_voice_driver_fill
+#define REST(loopname, len) \
+		subs	r4, r4, r8, lsl \#1	;\
+		movmi	r4, \#0			;\
+		strb	r4, [r0], r2		;\
+		add	r7, r7, \#len		;\
+		bic	r7, r7, r10		;\
+		subs	r6, r6, \#len		;\
+		beq	_fill			;\
+		cmp	r0, r1			;\
+		blt	loopname
+
+@ r0	=	start
+@ r1	=	end
+@ r2	=	inc
+@ r3	=	ptr to private data
+@   +4	=	pointer to voice_driver info
+
+_voice_driver_fill:
+		LDR	r3,[r3,#12]
+		LDMIA	r3,{r5-r11}
+		RSB	r8,r8,#127
+@ r5	=	wr_buf
+@ r6	=	wr_cnt
+@ r7	=	wr_tail
+@ r8	=	volume
+@ r9	=	conversion table
+@ r10	=	size
+		TEQ	r6,#0
+		BEQ	_fill1
+		TEQ	r9,#0
+		BEQ	_loopnone
+		TEQ	r11,#16
+		BEQ	_loop16S
+		TEQ	r11,#17
+		BEQ	_loop16U
+		TEQ	r11,#8
+		BEQ	_loop8S
+		TEQ	r11,#9
+		BEQ	_loop8U
+@ Log filler
+_looplog:	LDRB	r4,[r5,r7]
+		LDRB	r4,[r9,r4]
+		REST(_looplog, 1)
+		B	_endlp
+@ Vidc log filler (faster than log filler, and no volume)
+_loopnone:	LDRB	r4,[r5,r7]
+		REST(_loopnone, 1)
+		B	_endlp
+@ Signed 16-bit filler
+_loop16S:	LDR	r4,[r5,r7]
+		MOV	r4,r4, lsl #16
+		LDRB	r4,[r9,r4, lsr #19]
+		REST(_loop16S, 2)
+		B	_endlp
+@ Signed 8-bit filler
+_loop8S:	LDRB	r4,[r5,r7]
+		LDRB	r4,[r9,r4, lsl #5]
+		REST(_loop8S, 1)
+		B	_endlp
+@ Unsigned 16-bit filler
+_loop16U:	LDR	r4, [r5, r7]
+		MOV	r4, r4, lsl #16
+		EOR	r4, r4, #0x80000000
+		LDRB	r4, [r9, r4, lsr #19]
+		REST(_loop16U, 2)
+		B	_endlp
+@ Unsigned 8-bit filler
+_loop8U:	LDRB	r4,[r5,r7]
+		EOR	r4,r4,#0x80
+		LDRB	r4,[r9,r4, lsl #5]
+		REST(_loop8U, 1)
+
+_endlp:		STMIA	r3,{r5-r7}
+		MOV	r7,r10, lsr #2
+		ADD	r7,r7,r10, lsr #1
+		CMP	r6,r7
+		MOVGTS	pc,lr
+		ADD	r0,r3,#28
+		B	_wake_up_interruptible
+
+_fill:		CMP	r0,r1
+		BGE	_end
+_fill1:		MOV	r4,#0
+_fill2:		STRB	r4,[r0],r2
+		CMP	r0,r1
+		BLT	_fill2
+_end:		STMIA	r3,{r5-r7}
+		ADD	r0,r3,#28
+		B	_wake_up_interruptible
diff -urNwbB linux/arch/arm/drivers/sound/v_drv_wave.S linux.arm/arch/arm/drivers/sound/v_drv_wave.S
--- linux/arch/arm/drivers/sound/v_drv_wave.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/v_drv_wave.S	Sun Mar  3 12:57:56 1996
@@ -0,0 +1,65 @@
+/*
+ * linux/arch/arm/drivers/sound/v_drv_wave.s
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Voice low level filler - 256 byte wave table version.
+ *
+ * Uses a phase accumulator and a wave table.
+ */
+
+		.text
+		.global	_voice_driver_wave
+
+@ r0	=	start
+@ r1	=	end
+@ r2	=	inc
+@ r3	=	ptr to private data
+@   +4	=	pointer to voice_driver info
+
+_voice_driver_wave:
+		LDR	r10,[r3,#12]
+		LDMIA	r10,{r5-r9}
+		RSB	r8,r8,#127
+@ r5	=	
+@ r6	=	duration
+@ r7	=	phase accumulator
+@ r8	=	volume
+@ r9	=	wave table
+@ r10	=	
+@ r11	=	
+_loop:		LDRB	r4,[r9,r7, lsr #24]
+		ADD	r7,r7,r7, lsl #16
+		SUBS	r4,r4,r8, lsl #1
+		MOVMI	r4,#0
+		STRB	r4,[r0],r2
+		SUBS	r6,r6,#1
+		MOVMI	r6,#0
+		LDRB	r4,[r9,r7, lsr #24]
+		ADD	r7,r7,r7, lsl #16
+		SUBS	r4,r4,r8, lsl #1
+		MOVMI	r4,#0
+		STRB	r4,[r0],r2
+		SUBS	r6,r6,#1
+		MOVMI	r6,#0
+		LDRB	r4,[r9,r7, lsr #24]
+		ADD	r7,r7,r7, lsl #16
+		SUBS	r4,r4,r8, lsl #1
+		MOVMI	r4,#0
+		STRB	r4,[r0],r2
+		SUBS	r6,r6,#1
+		MOVMI	r6,#0
+		LDRB	r4,[r9,r7, lsr #24]
+		ADD	r7,r7,r7, lsl #16
+		SUBS	r4,r4,r8, lsl #1
+		MOVMI	r4,#0
+		STRB	r4,[r0],r2
+		SUBS	r6,r6,#1
+		MOVMI	r6,#0
+		CMP	r0,r1
+		BLT	_loop
+		STMIA	r10,{r5-r7}
+		TEQ	r6,#0
+		MOVNES	pc,lr
+		MOV	r0,r3
+		LDR	pc,[r3,#8]		@ call gate off
diff -urNwbB linux/arch/arm/drivers/sound/voice.c linux.arm/arch/arm/drivers/sound/voice.c
--- linux/arch/arm/drivers/sound/voice.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice.c	Sun Mar  3 12:53:57 1996
@@ -0,0 +1,176 @@
+/*
+ * linux/arch/arm/drivers/sound/voice.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Voice driver
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include "soundcont.h"
+#include "soundinit.h"
+#include "soundirq.h"
+#include "voice.h"
+
+extern const struct voice_driver voice_sample;
+extern const struct voice_driver voice_wave;
+
+/*
+ * Allocate a voice driver of type 'type', passing 'data' to it
+ */
+
+int voice_allocate(int type,int data)
+{
+	switch(type)
+	{
+		case VOICE_SAMPLE: return voice_sample.vd_alloc(data);
+		case VOICE_WAVE:   return voice_wave.vd_alloc(data);
+		default:	   return -ENODEV;
+	}
+}
+
+/*
+ * Reallocate the buffer associated with driver 'vd'
+ *
+ * This routine can be called while the driver is in use,
+ * however, it may wait...
+ *
+ * If unsuccessful, returns -ENOMEM, but sample will keep playing
+ *
+ */
+
+int voice_realloc(struct voice_driver *vd, int newlength)
+{
+	char *nb,*ob;
+	int oldlength;
+	unsigned long flags;
+
+	nb = (char *)kmalloc(newlength, GFP_KERNEL);
+	if(!nb)
+		return -ENOMEM;
+
+/* Now for the trick */
+	save_flags(flags);
+	cli();
+	ob = vd->vd_buf;
+	oldlength=vd->vd_size;
+	if(vd->vd_tail<vd->vd_head)
+	{/* Simple case */
+		memcpy(nb, ob+vd->vd_tail, vd->vd_cnt);
+		vd->vd_buf=nb;
+		vd->vd_size=newlength;
+	}
+	else
+	{/* Complicated case - copy top part to top of new buffer, then same down the bottom */
+		int newtail=newlength+vd->vd_tail-oldlength;
+		memcpy(nb+newtail, ob+vd->vd_tail, oldlength-vd->vd_tail);
+		memcpy(nb, ob, vd->vd_head);
+		vd->vd_buf=nb;
+		vd->vd_tail=newtail;
+		vd->vd_size=newlength;
+	}
+	kfree_s(ob,oldlength);
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Install a voice on a channel
+ */
+
+int voice_install(struct voice_driver *vd, int channel)
+{
+	struct voice *vdrv;
+
+	if(vd->vd_channel != -1)
+		return -EINVAL;
+	vd->vd_channel = channel;
+	vdrv = (struct voice *)kmalloc(sizeof(struct voice), GFP_KERNEL);
+	if(!vdrv)
+		return -ENOMEM;
+	
+	vd->vd_install(vd, vdrv);
+
+	sound_voice[channel] = vdrv;
+	sound_allocated[channel] = 1;
+	soundirq_inc();
+	return 0;
+}
+
+/* 
+ * find a free channel or -1 on error (channel = -1 to find, channel no to check)
+ */
+
+int voice_findfree(int channel)
+{
+	for(; channel < 8; channel++)
+		if(!sound_allocated[channel])
+			return channel;
+	return -1;
+}
+
+/*
+ * Remove a voice from a channel
+ */
+
+void voice_remove(struct voice_driver *vd)
+{
+	struct voice *vdrv;
+
+	if(vd->vd_channel == -1)
+		return;
+	vdrv = sound_voice[vd->vd_channel];
+	
+	if(vd->vd_remove)
+		vd->vd_remove(vd, vdrv);
+	
+	sound_voice[vd->vd_channel] = &sound_to_quiet_voice;
+	sound_allocated[vd->vd_channel] = 0;
+	vd->vd_channel = -1;
+
+	kfree_s(vdrv, sizeof(struct voice));
+}
+
+/*
+ * Release a voice, and return its memory
+ */
+
+int voice_release(struct voice_driver *vd)
+{
+	struct voice *vdrv;
+
+	if(vd->vd_channel != -1)
+	{
+		vdrv = sound_voice[vd->vd_channel];
+
+		if(vd->vd_remove)
+			vd->vd_remove(vd, vdrv);
+
+		sound_voice[vd->vd_channel] = &sound_to_quiet_voice;
+		sound_allocated[vd->vd_channel] = 0;
+		vd->vd_channel = -1;
+
+		kfree_s(vdrv, sizeof(struct voice));
+	}
+
+	if(vd->vd_buf)
+		kfree_s(vd->vd_buf,vd->vd_size);
+	kfree_s(vd, sizeof(struct voice_driver));
+	return 0;
+}
+
+/*
+ * Change the volume on a channel
+ */
+
+int voice_volume(struct voice_driver *vd,int newvolume)
+{
+	if(vd->vd_vol)
+		return vd->vd_vol(vd,newvolume);
+	if(newvolume == -1)
+		return vd->vd_volume;
+	vd->vd_volume=newvolume;
+	return 0;
+}
diff -urNwbB linux/arch/arm/drivers/sound/voice.h linux.arm/arch/arm/drivers/sound/voice.h
--- linux/arch/arm/drivers/sound/voice.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice.h	Sun Feb 11 09:32:36 1996
@@ -0,0 +1,95 @@
+/*
+ * voice.h
+ *
+ * Voice generator header file
+ *
+ */
+
+#ifndef __sound_voice_h
+#define __sound_voice_h
+
+#ifndef __sound_config_h
+#include "config.h"
+#endif
+
+#ifndef __sound_soundirq_h
+#include "soundirq.h"
+#endif
+
+/*
+ * The structure voice_driver is hard coded into voice.s.
+ * Don't add anything before the voice data comment without
+ * changing voice.s.
+ */
+
+struct voice_driver
+{
+/* Used by irq - level routines */
+	char *vd_buf;				/* Computed sample waveform buffer / index */
+	int vd_cnt;				/* Number of samples left in buffer */
+#define vd_dur vd_cnt
+	int vd_tail;				/* Tail of circulat buffer */
+#define vd_phase vd_tail
+	int vd_volume;				/* This voice's volume */
+	const unsigned char *vd_convtable;	/* Conversion table (can be wave table) */
+	int vd_size;				/* Size of waveform buffer (power of 2) */
+	int vd_bits;				/* Bits per sample */
+	struct wait_queue *vd_wait;		/* Wait queue for writes */
+/* Voice data */
+	int vd_head;				/* Head of circular buffer */
+	int vd_channel;				/* Channel that this voice is attached */
+	int (*vd_write)(struct voice_driver *vd, const char *b, int nr);
+						/* Writing routine */
+	int (*vd_alloc)(int data);		/* Allocation routine */
+	int (*vd_install)(struct voice_driver *vd, struct voice *vdrv);
+						/* Install voice driver */
+	void (*vd_remove)(struct voice_driver *vd, struct voice *vdrv);
+						/* Remove a voice from a channel */
+	int (*vd_vol)(struct voice_driver *vd,int newvol);
+						/* Change channel volume */
+};
+
+/*
+ * Allocate a voice generator
+ * type: see below
+ * data: Unused at the moment,
+ */
+extern int voice_allocate(int type,int data);
+#define VOICE_SAMPLE	1
+#define VOICE_WAVE	2
+#define VOICE_SINE	3
+
+/*
+ * Reallocate the buffer used by the voice generator
+ */
+extern int voice_realloc(struct voice_driver *vd, int newsize);
+
+/*
+ * Install a voice generator on a channel
+ * This doesn't enable the channel though!
+ */
+extern int voice_install(struct voice_driver *vd,int channel);
+
+/*
+ * find a free channel or -1 on error (channel = -1 to find, channel no to check)
+ */
+extern int voice_findfree(int channel);
+
+/*
+ * Remove a voice generator from a channel,
+ * installing a quiet voice generator.
+ */
+extern void voice_remove(struct voice_driver *vd);
+
+/*
+ * Release a voice generator (installs a quiet generator)
+ * and free its memory
+ */
+extern int voice_release(struct voice_driver *vd);
+
+/*
+ * Change the volume on a channel
+ */
+extern int voice_volume(struct voice_driver *vd, int newvolume);
+
+#endif
diff -urNwbB linux/arch/arm/drivers/sound/voice_samp.c linux.arm/arch/arm/drivers/sound/voice_samp.c
--- linux/arch/arm/drivers/sound/voice_samp.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice_samp.c	Sun Mar  3 12:55:04 1996
@@ -0,0 +1,93 @@
+/*
+ * linux/arch/arm/drivers/sound/voice_samp.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Sample voice driver.
+ *
+ * This voice driver takes is sound data from the data being written to it.
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include "soundinit.h"
+#include "soundirq.h"
+#include "voice.h"
+
+#include <asm/segment.h>
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+static int voice_sample_write(struct voice_driver *vd, const char *b, int count)
+{
+	int c, total =0;
+	unsigned long flags;
+
+	save_flags(flags);
+	while(1) {
+		cli();
+		c = MIN(count, MIN(vd->vd_size - vd->vd_cnt - 1,
+				   vd->vd_size - vd->vd_head));
+		if(c<=0)
+			break;
+
+		memcpy_fromfs(vd->vd_buf + vd->vd_head, b, c);
+		vd->vd_head = (vd->vd_head + c) & (vd->vd_size-1);
+		vd->vd_cnt += c;
+		restore_flags(flags);
+		b += c;
+		count -= c;
+		total += c;
+	}
+	restore_flags(flags);
+	return total;
+}
+
+static int voice_sample_alloc(int data)
+{
+	struct voice_driver *vd;
+	extern struct voice_driver voice_sample;
+
+	vd = (struct voice_driver *)kmalloc(sizeof(struct voice_driver), GFP_KERNEL);
+	if(!vd)
+		return -ENOMEM;
+
+	memcpy(vd, &voice_sample, sizeof(struct voice_driver));
+
+	vd->vd_buf = (char *)kmalloc(vd->vd_size, GFP_KERNEL);
+	return (int)vd;
+}
+
+static int voice_sample_install(struct voice_driver *vd, struct voice *vdrv)
+{
+	extern void voice_driver_fill(char *, char *, char);
+	
+	vdrv->fill	= voice_driver_fill;
+	vdrv->gateon	= NULL;
+	vdrv->gateoff	= NULL;
+	vdrv->data[0]	= (int)vd;
+	
+	return 0;
+}
+
+const struct voice_driver voice_sample =
+{
+	NULL,			/* Buffer */
+	0,			/* Count */
+	0,			/* Tail */
+	127,			/* Volume */
+	NULL,			/* Conversion table */
+	4096,			/* Buffer size */
+	8,			/* Bits/sample */
+	NULL,			/* Wait queue */
+	0,			/* Head */
+	-1,			/* Channel */
+	voice_sample_write,	/* Write sample data */
+	voice_sample_alloc,	/* Allocate new voice */
+	voice_sample_install,	/* Install voice driver */
+	NULL,			/* Remove voice driver (default) */
+	NULL			/* Change channel volume */
+};
diff -urNwbB linux/arch/arm/drivers/sound/voice_wave.c linux.arm/arch/arm/drivers/sound/voice_wave.c
--- linux/arch/arm/drivers/sound/voice_wave.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice_wave.c	Sun Mar  3 12:55:50 1996
@@ -0,0 +1,136 @@
+/*
+ * linux/arch/arm/drivers/sound/voice_wave.c
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Wave table voice driver.
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include "soundinit.h"
+#include "soundirq.h"
+#include "voice.h"
+
+#include <asm/segment.h>
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+static int voice_wave_write(struct voice_driver *vd, const char *b, int count)
+{
+	int c, total =0;
+	unsigned long flags;
+
+	save_flags(flags);
+	while(1) {
+		cli();
+		c = MIN(count, MIN(vd->vd_size - vd->vd_cnt - 1,
+				   vd->vd_size - vd->vd_head));
+		if(c<=0)
+			break;
+
+		memcpy_fromfs(vd->vd_buf + vd->vd_head, b, c);
+		vd->vd_head = (vd->vd_head + c) & (vd->vd_size-1);
+		vd->vd_cnt += c;
+		restore_flags(flags);
+		b += c;
+		count -= c;
+		total += c;
+	}
+	restore_flags(flags);
+	return total;
+}
+
+static int voice_wave_alloc(int data)
+{
+	struct voice_driver *vd;
+	extern struct voice_driver voice_wave;
+
+	vd = (struct voice_driver *)kmalloc(sizeof(struct voice_driver), GFP_KERNEL);
+	if(!vd)
+		return -ENOMEM;
+
+	memcpy(vd, &voice_wave, sizeof(struct voice_driver));
+
+	vd->vd_buf = NULL;
+	return (int)vd;
+}
+
+static void voice_wave_gateoff(struct voice *vdrv)
+{
+	struct voice_driver *vd;
+	
+	vd = (struct voice_driver *)vdrv->data[0];
+	if(!vd->vd_buf)
+		voice_release(vd);
+}
+
+static int voice_wave_install(struct voice_driver *vd, struct voice *vdrv)
+{
+	extern void voice_driver_wave(char *, char *, char);
+	
+	vdrv->fill	= voice_driver_wave;
+	vdrv->gateon	= NULL;
+	vdrv->gateoff	= voice_wave_gateoff;
+	vdrv->data[0]	= (int)vd;
+	
+	return 0;
+}
+
+unsigned char voice_wave_sine[]=
+{
+	0x00, 0x5A, 0x76, 0x88, 0x96, 0xA2, 0xA8, 0xAE,
+	0xB4, 0xBA, 0xC0, 0xC4, 0xC6, 0xCA, 0xCC, 0xD0,
+	0xD2, 0xD6, 0xD8, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4,
+	0xE4, 0xE6, 0xE8, 0xE8, 0xEA, 0xEC, 0xEC, 0xEE,
+	0xEE, 0xF0, 0xF0, 0xF2, 0xF2, 0xF4, 0xF4, 0xF6,
+	0xF6, 0xF8, 0xF8, 0xF8, 0xFA, 0xFA, 0xFC, 0xFC,
+	0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+	0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+	0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+	0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC,
+	0xFC, 0xFC, 0xFC, 0xFA, 0xFA, 0xF8, 0xF8, 0xF8,
+	0xF6, 0xF6, 0xF4, 0xF4, 0xF2, 0xF2, 0xF0, 0xF0,
+	0xEE, 0xEE, 0xEC, 0xEC, 0xEA, 0xE8, 0xE8, 0xE6,
+	0xE4, 0xE4, 0xE2, 0xE0, 0xDE, 0xDC, 0xD8, 0xD6,
+	0xD2, 0xD0, 0xCC, 0xCA, 0xC6, 0xC4, 0xC0, 0xBA,
+	0xB4, 0xAE, 0xA8, 0xA2, 0x96, 0x88, 0x76, 0x5A,
+	0x00, 0x5D, 0x79, 0x89, 0x97, 0xA3, 0xA9, 0xAF,
+	0xB5, 0xBB, 0xC1, 0xC5, 0xC7, 0xCB, 0xCD, 0xD1,
+	0xD3, 0xD7, 0xD9, 0xDD, 0xDF, 0xE1, 0xE3, 0xE5,
+	0xE5, 0xE7, 0xE9, 0xE9, 0xEB, 0xED, 0xED, 0xEF,
+	0xEF, 0xF1, 0xF1, 0xF3, 0xF3, 0xF5, 0xF5, 0xF7,
+	0xF7, 0xF9, 0xF9, 0xF9, 0xFB, 0xFB, 0xFD, 0xFD,
+	0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD,
+	0xFD, 0xFD, 0xFD, 0xFB, 0xFB, 0xF9, 0xF9, 0xF9,
+	0xF7, 0xF7, 0xF5, 0xF5, 0xF3, 0xF3, 0xF1, 0xF1,
+	0xEF, 0xEF, 0xED, 0xED, 0xEB, 0xE9, 0xE9, 0xE7,
+	0xE5, 0xE5, 0xE3, 0xE1, 0xDF, 0xDD, 0xD9, 0xD7,
+	0xD3, 0xD1, 0xCD, 0xCB, 0xC7, 0xC5, 0xC1, 0xBB,
+	0xB5, 0xAF, 0xA9, 0xA3, 0x97, 0x89, 0x79, 0x5D
+};
+
+const struct voice_driver voice_wave =
+{
+	NULL,			/* Buffer (release at end/don't release) */
+	0,			/* Count  (duration) */
+	0,			/* Tail   (phase accumulator) */
+	127,			/* Volume */
+	voice_wave_sine,	/* Wave table */
+	4096,			/* Buffer size */
+	8,			/* Bits/sample */
+	NULL,			/* Wait queue */
+	0,			/* Head */
+	-1,			/* Channel */
+	voice_wave_write,	/* Write sample data */
+	voice_wave_alloc,	/* Allocate new voice */
+	voice_wave_install,	/* Install voice driver */
+	NULL,			/* Remove voice driver (default) */
+	NULL			/* Change channel volume */
+};
diff -urNwbB linux/arch/arm/kernel/Makefile linux.arm/arch/arm/kernel/Makefile
--- linux/arch/arm/kernel/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/Makefile	Sun Feb 11 09:32:37 1996
@@ -0,0 +1,23 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.S.o:
+	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+
+all: kernel.o head.o
+
+O_TARGET := kernel.o
+O_OBJS   := process.o signal.o traps.o irq.o ptrace.o \
+	    ioport.o arm.o setup.o time.o ecard.o sys-arm.o \
+	    dma.o
+
+head.o: head.S $(TOPDIR)/include/linux/tasks.h
+	$(CC) -D__ASSEMBLY__ -traditional -c $*.S -o $*.o
+
+include $(TOPDIR)/Rules.make
diff -urNwbB linux/arch/arm/kernel/arm.c linux.arm/arch/arm/kernel/arm.c
--- linux/arch/arm/kernel/arm.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/arm.c	Sun Mar  3 13:02:25 1996
@@ -0,0 +1,42 @@
+/*
+ * linux/arch/arm/kernel/arm.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/linkage.h>
+
+asmlinkage void arm_syscall(int no, struct pt_regs *regs)
+{
+    switch (no) {
+    case 1: /* set 32-bit user mode */
+	printk ("%s [%d]: attempt to switch to 32-bit mode unsupported\n",
+		current->comm, current->pid);
+	send_sig (SIGILL, current, 1);
+	break;
+    case 0: /* branch through 0 */
+    default:
+	printk ("%s [%d]: arm syscall %d\n", current->comm, current->pid, no);
+	send_sig (SIGILL, current, 1);
+    }
+}
+
+asmlinkage void deferred(int n)
+{
+    printk("%s [%d]: old system call %X\n", current->comm, current->pid, n);
+}
+
+asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr)
+{
+    printk("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
+}
+
+asmlinkage void arm_invalidptr(const char *str, void *pc, int size)
+{
+    printk("Invalid pointer size in %s: %p (PC=%08X)\n", str, pc, size);
+}
diff -urNwbB linux/arch/arm/kernel/dma.c linux.arm/arch/arm/kernel/dma.c
--- linux/arch/arm/kernel/dma.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/dma.c	Sun Mar  3 13:02:49 1996
@@ -0,0 +1,87 @@
+/*
+ * linux/arch/arm/kernel/dma.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+static unsigned long dma_address[8];
+static unsigned long dma_count[8];
+static char dma_direction[8] = { -1, -1, -1, -1, -1, -1, -1};
+
+void enable_dma (unsigned int dmanr)
+{
+    if (dmanr == 2) {
+    	switch (dma_direction[dmanr]) {
+    	case 1: /* read */
+    	    {
+		extern unsigned char floppy_fiqin_start, floppy_fiqin_end;
+		extern void floppy_fiqsetup (unsigned long len, unsigned long addr,
+			unsigned long port);
+		memcpy ((void *)0x1c, (void *)&floppy_fiqin_start,
+			&floppy_fiqin_end - &floppy_fiqin_start);
+		floppy_fiqsetup (dma_count[dmanr], dma_address[dmanr], io_base + 0x02a000);
+		enable_irq (16);
+		break;
+	    }
+	case 0: /* write */
+	    {
+		extern unsigned char floppy_fiqout_start, floppy_fiqout_end;
+		extern void floppy_fiqsetup (unsigned long len, unsigned long addr,
+			unsigned long port);
+		memcpy ((void *)0x1c, (void *)&floppy_fiqout_start,
+			&floppy_fiqout_end - &floppy_fiqout_start);
+		floppy_fiqsetup (dma_count[dmanr], dma_address[dmanr], io_base + 0x02a000);
+		enable_irq (16);
+		break;
+	    }
+    	default:
+	    printk ("enable_dma: dma%d not initialised\n", dmanr);
+	    return;
+	}
+    }
+}
+
+void set_dma_mode (unsigned int dmanr, char mode)
+{
+    if (dmanr < 8) {
+	if (mode == DMA_MODE_READ)
+	    dma_direction[dmanr] = 1;
+	else
+	if (mode == DMA_MODE_WRITE)
+	    dma_direction[dmanr] = 0;
+	else
+	    printk ("set_dma_mode: dma%d: invalid mode %02X not supported\n",
+		dmanr, mode);
+    } else
+	printk ("set_dma_mode: dma %d not supported\n", dmanr);
+}
+
+void set_dma_addr (unsigned int dmanr, unsigned int addr)
+{
+    if (dmanr < 8)
+	dma_address[dmanr] = (unsigned long)addr;
+    else
+	printk ("set_dma_addr: dma %d not supported\n", dmanr);
+}
+
+void set_dma_count (unsigned int dmanr, unsigned int count)
+{
+    if (dmanr < 8)
+	dma_count[dmanr] = (unsigned long)count;
+    else
+    	printk ("set_dma_count: dma %d not supported\n", dmanr);
+}
+
+int get_dma_residue (unsigned int dmanr)
+{
+    extern int floppy_fiqresidual (void);
+    if (dmanr == 2)
+	return floppy_fiqresidual ();
+
+    return -1;
+}
diff -urNwbB linux/arch/arm/kernel/ecard.c linux.arm/arch/arm/kernel/ecard.c
--- linux/arch/arm/kernel/ecard.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/ecard.c	Sat Feb 24 18:16:01 1996
@@ -0,0 +1,307 @@
+/*
+ * linux/arch/arm/kernel/ecard.c
+ *
+ * Find all installed expansion cards
+ *
+ * Copyright 1995 Russell King
+ *
+ * Information from Acorns RiscOS3 PRMs
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/irq-no.h>
+#include <asm/ecard.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+/*
+ * from linux/arch/arm/kernel/irq.c
+ */
+extern void do_fast_IRQ(int irq);
+extern unsigned char *ioc;
+
+#define MAX_ECARDS 4
+
+#define expmask ((volatile unsigned char *)(io_base + 0x360000))
+
+static struct expansion_card expcard[MAX_ECARDS];
+static char have_expmask = 0;
+static char initialised = 0;
+static unsigned long kmem;
+
+void *ecard_malloc(int len)
+{
+    int r;
+
+    len = (len + 3) & ~3;
+
+    if(!initialised) {
+	r = kmem;
+	kmem += len;
+	return (void *)r;
+    } else
+	return kmalloc(len, GFP_KERNEL);
+}
+
+static void expansioncard_irq_noexpmask(int intr_no, struct pt_regs *regs)
+{
+    int i;
+
+    for(i=0; i<MAX_ECARDS; i++) {
+	if (expcard[i].r_found && expcard[i].r_claimed && expcard[i].irq) {
+	    if (expcard[i].r_irqmask) {
+		if(expcard[i].r_irqaddr[0] & expcard[i].r_irqmask)
+		    do_fast_IRQ(expcard[i].irq);
+		else
+		    	printk("Wild interrupt from expansion card %d\n", i);
+	    } else
+		do_fast_IRQ (expcard[i].irq);
+	}
+    }
+}
+
+static void expansioncard_irq_expmask (int intr_no, struct pt_regs *regs)
+{
+    int i;
+#if 0
+    int oldexpmask;
+#endif
+
+    for (i = 0; i < MAX_ECARDS; i++) {
+	if (expmask[0] & (1 << i)) {
+	    if (expcard[i].r_found && expcard[i].r_claimed) {
+		if (!expcard[i].r_irqmask || (expcard[i].r_irqaddr[0] & expcard[i].r_irqmask)) {
+		    /*
+		     * this ugly code is so that we can operate a prioritorising system.
+		     * Card 0 	highest priority
+		     * Card 1
+		     * Card 2
+		     * Card 3	lowest priority
+		     * Serial cards should go in 0/1, ethernet/scsi in 2/3
+		     * otherwise you will loose serial data at high speeds!
+		     *
+		     * It doesn't appear to work!  I don't think the hardware
+		     * likes me messing around with this register too much...
+		     */
+#if 0
+		    cli ();
+		    oldexpmask = have_expmask;
+		    have_expmask &= (~(15 << i)) | 0xf0;
+		    expmask[4] = have_expmask;
+		    ioc[0x28] |= 1 << 5;
+		    sti ();
+#endif
+		    do_fast_IRQ (expcard[i].irq);
+#if 0
+		    cli ();
+		    ioc[0x28] &= ~(1 << 5);
+		    expmask[4] = oldexpmask;
+		    sti ();
+#endif
+		} else
+		    printk ("card%d: hardware error: interrupt?\n", i);
+	    } else {
+		printk ("card%d: interrupt from non-claimed card\n", i);
+		have_expmask &= ~(1 << i);
+		/* disable interrupt */
+		expmask[4] = have_expmask;
+	    }
+	}
+    }
+}
+
+void ecard_enableirq (unsigned int intr_no)
+{/*printk ("irq %d enable\n", intr_no);
+    if (have_expmask && intr_no >= 24 && intr_no < 28) {
+	have_expmask |= 1 << (intr_no - 24);
+	expmask[4] = have_expmask;
+    }*/
+}
+
+void ecard_disableirq (unsigned int intr_no)
+{/*printk ("irq %d disable\n", intr_no);
+    if (have_expmask && intr_no >= 24 && intr_no < 28) {
+	have_expmask &= ~(1 << (intr_no - 24));
+	expmask[4] = have_expmask; 
+    }*/
+}
+
+static int ecard_checkirqhw(void)
+{
+	int found;
+	expmask[4] = 0x00;
+	expmask[0] = 0xff;
+	found = (expmask[0] == 0xf0);
+	expmask[4] = 0xff;
+	return found;
+}
+
+static void ecard_readbytes(void *addr, struct expansion_card *ec, int off, int len, int useld)
+{
+	extern int read_loader(int off, volatile unsigned char *pa, unsigned char *loader);
+  
+	unsigned char *a = (unsigned char *)addr;
+
+	if(!useld || !ec->r_loader) {
+		while(len--)
+			*a++ = ec->r_podaddr[(off++)*4];
+	} else {
+		while(len--) {
+			*(unsigned long *)0x108 = 0; /* hack for some loaders!!! */
+			*a++ = read_loader(off++, ec->r_podaddr, ec->r_loader);
+		}
+	}
+}
+
+struct expansion_card *ecard_find(int cld, int num, int prod[], int manu[])
+{
+	int i, j;
+
+	for (i = 0 ; i<MAX_ECARDS; i++) {
+		if(expcard[i].r_found == 0 || expcard[i].r_claimed)
+			continue;
+		if((e_ecld(&expcard[i].ecld) & 0x78) != (cld & 0x78))
+			continue;
+		if((e_ecld(&expcard[i].ecld) & 0x78) != 0)
+			return &expcard[i];
+		for (j = 0; j<num; j++) {
+			if(e_prod(&expcard[i].ecld) == prod[j] &&
+				e_manu(&expcard[i].ecld) == manu[j])
+				return &expcard[i];
+		}
+	}
+	return NULL;
+}
+
+int ecard_readchunk(struct chunk_dir *cd, struct expansion_card *ec, int id, int num)
+{
+	int index = 16;
+	int useld = 0;
+
+	while(1) {
+		ecard_readbytes((unsigned char *)cd, ec, index, 8, useld);
+		index += 8;
+		if(c_id(cd) == 0) {
+			if(!useld && ec->r_loader) {
+				useld = 1;
+				index = 0;
+				continue;
+			}
+			return 0;
+		}
+		if(c_id(cd) == 0xf0) { /* link */
+			index = c_start(cd);
+			continue;
+		}
+		if(c_id(cd) == 0x80) { /* loader */
+			if(!ec->r_loader) {
+				ec->r_loader = ecard_malloc(c_len(cd));
+				ecard_readbytes(ec->r_loader, ec, (int)c_start(cd), c_len(cd), useld);
+			}
+			continue;
+		}
+		if(c_id(cd) == id && num-- == 0)
+			break;
+	}
+
+	if(c_id(cd) & 0x80) {
+		switch(c_id(cd) & 0x70) {
+		case 0x70:
+			ecard_readbytes((unsigned char *)cd->d.string, ec,
+					(int)c_start(cd), c_len(cd), useld);
+			break;
+		case 0x00:
+			break;
+		}
+	}
+	return 1;
+}
+
+unsigned long ecard_init(unsigned long my_kmem)
+{
+    int i;
+    kmem = (my_kmem | 3) + 1;
+    initialised = 0;
+
+    for (i=0; i<MAX_ECARDS; i++) {
+	expcard[i].r_claimed = 0;
+	expcard[i].r_found = 0;
+    }
+
+    if (ecard_checkirqhw()) {
+	have_expmask = 0xff;
+	printk("Expansion card interrupt management hardware found\n");
+    }
+
+    if (request_irq(IRQ_EXPANSIONCARD, have_expmask ? expansioncard_irq_expmask :
+		expansioncard_irq_noexpmask, 0, "expansion cards") != 0) {
+	printk("Could not allocate interrupt for expansion cards\n");
+	return kmem;
+    }
+	
+    printk("Searching for expansion cards:");
+
+    for (i=0; i<MAX_ECARDS; i++) {
+	struct expansion_card *ec;
+		
+	ec = &expcard[i];
+		
+	ec->r_podaddr = (volatile unsigned char *)(io_base + 0x3C0000 + 0x4000*i);
+		
+	ec->ecld.r_ecld = 2;
+	ec->irq = 0;
+	ec->fiq = 0;
+		
+	ecard_readbytes(&ec->ecld, ec, 0, 16, 0);
+		
+	if (ec->ecld.r_ecld & 2) {
+	    have_expmask &= ~(1<<i);
+	    continue;
+	}
+
+	ec->irq = 24 + i;
+	ec->fiq = 28 + i;
+	ec->r_fiqaddr = ec->r_irqaddr = ec->r_podaddr;
+	ec->r_fiqmask = 4;
+	ec->r_irqmask = 1;
+		
+	if ((e_ecld(&ec->ecld) & 0x78) == 0) {
+	    if (e_is(&ec->ecld)) {
+		ec->r_fiqmask = e_fiqmask (&ec->ecld);
+		ec->r_irqmask = e_irqmask (&ec->ecld);
+		ec->r_fiqaddr = ec->r_podaddr + e_fiqaddr (&ec->ecld);
+		ec->r_irqaddr = ec->r_podaddr + e_irqaddr (&ec->ecld);
+	    }
+	    if (e_cd(&ec->ecld)) {
+		struct chunk_dir cd;
+		if (ecard_readchunk(&cd, ec, 0xf5, 0))
+		    printk ("\n  %d: [%04X:%04X] %s", i,
+			e_manu(&ec->ecld), e_prod(&ec->ecld),
+			cd.d.string);
+	    } else
+		printk ("\n  %d: [%04X:%04X] *Unknown*", i, e_manu(&ec->ecld),
+			e_prod(&ec->ecld));
+	} else
+	    printk("\n  %d: Simple card %d\n", i, (e_ecld(&ec->ecld) & 0x78) >> 3);
+	ec->r_found = 1;
+    }
+    printk("\n");
+
+    if (have_expmask)
+	expmask[4] = have_expmask;
+
+    initialised = 1;
+    return kmem;	
+}
+
+unsigned long bios32_init(unsigned long start_mem, unsigned long end_mem)
+{
+    return ecard_init(start_mem);
+}
diff -urNwbB linux/arch/arm/kernel/head.S linux.arm/arch/arm/kernel/head.S
--- linux/arch/arm/kernel/head.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/head.S	Sun Mar  3 13:03:43 1996
@@ -0,0 +1,105 @@
+/*
+ * linux/arch/arm/kernel/entry.S
+ *
+ * Copyright (C) 1994, 1995, 1996 Russell King
+ *
+ * Kernel startup code
+ */
+
+		.text
+		.align
+
+@ Entry point and restart point.  Entry *must* be called with r0 != 0
+@ r1 is length of kernel
+
+		.global	__stext
+__stext:
+		b	__entry
+
+LC0:
+		.word	_edata
+LC1:
+		.word	_end
+LC2:
+		.word	_params
+LC3:
+		.word	_init_user_stack
+LC4:
+		.word	_arm_id
+		.align
+
+		.global	__entry
+__entry:
+@ Move parameter page if it is defined (old loaders passed r0 pointing to params)
+@ to where the kernel expects to see it.  Now, r0 == 0 always on entry.
+
+		teq	r0, #0
+		beq	newparams
+		mov	r4, #0x02000000
+		add	r4, r4, #0x0007C000
+		mov	r3, #0x4000
+		sub	r3, r3, #4
+		mov	r2, r0
+lp2:		ldmia	r2!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		stmia	r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
+		subs	r3, r3, #32
+		bpl	lp2
+		mov	r0, #0
+
+@ Clear kernels bss
+
+newparams:	ldr	r3, [pc, #LC0 - . - 8]
+		ldr	r4, [pc, #LC1 - . - 8]
+		mov	r2, #0
+clrzilp:	cmp	r3, r4
+		strcc	r2, [r3],#4
+		bcc	clrzilp
+
+@ Setup stack
+		ldr	sp, [pc, #LC3 - . - 8]
+		add	sp, sp, #4096
+		mov	sl, r4
+@
+@ Read processor ID register (CP#15, CR0).
+@ NOTE: ARM2 & ARM250 cause an undefined instruction exception...
+@ Values are:	4156061X = ARM 610
+@ 		4156030X = ARM 3
+@		4156025X = ARM 250
+@		4156020X = ARM 2
+@
+		adr	r0, undef_instr - 12
+		mov	r2, #4
+		mov	r3, #0xea000000
+		orr	r0, r3, r0, lsr #2
+		str	r0, [r2]
+
+		mrc	15, 0, r0, c0, c0
+
+continue:
+@
+@ Make undefined instr vector point to itself (endless loop)
+@
+		sub	r3, r3, #2
+		orr	r3, r3, #0xeb000000
+		str	r3, [r2]
+		ldr	r3, [pc, #LC4 - . - 8]
+		str	r0, [r3]
+		mov	fp, #0
+		b	_start_kernel
+@
+@ IF we come in here from the mrc instruction, then we have an ARM2/250
+@
+undef_instr:	adr	r0, undef_instr2 - 12
+		orr	r0, r3, r0, lsr #2
+		str	r0, [r2], #4
+		swpb	r0, r0, [r2]
+		ldr 	r0, arm250_id
+		b	continue
+
+arm250_id:	.long	0x41560250
+
+undef_instr2:	ldr	r0, arm2_id
+		b	continue
+
+arm2_id:	.long	0x41560200
+
diff -urNwbB linux/arch/arm/kernel/ioport.c linux.arm/arch/arm/kernel/ioport.c
--- linux/arch/arm/kernel/ioport.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/ioport.c	Sun Mar  3 13:04:23 1996
@@ -0,0 +1,98 @@
+/*
+ * linux/arch/arm/kernel/ioport.c
+ *
+ * This contains the io-permission bitmap code - written by obz, with changes
+ * by Linus.
+ *
+ * Modifications for ARM processor Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+
+/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
+asmlinkage void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
+{
+	int mask;
+	unsigned long *bitmap_base = bitmap + (base >> 5);
+	unsigned short low_index = base & 0x1f;
+	int length = low_index + extent;
+
+	if (low_index != 0) {
+		mask = (~0 << low_index);
+		if (length < 32)
+				mask &= ~(~0 << length);
+		if (new_value)
+			*bitmap_base++ |= mask;
+		else
+			*bitmap_base++ &= ~mask;
+		length -= 32;
+	}
+
+	mask = (new_value ? ~0 : 0);
+	while (length >= 32) {
+		*bitmap_base++ = mask;
+		length -= 32;
+	}
+
+	if (length > 0) {
+		mask = ~(~0 << length);
+		if (new_value)
+			*bitmap_base++ |= mask;
+		else
+			*bitmap_base++ &= ~mask;
+	}
+}
+
+/*
+ * this changes the io permissions bitmap in the current task.
+ */
+asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
+{
+	if (from + num <= from)
+		return -EINVAL;
+#ifndef __arm__
+	if (from + num > IO_BITMAP_SIZE*32)
+		return -EINVAL;
+#endif
+	if (!suser())
+		return -EPERM;
+
+#ifdef IODEBUG
+	printk("io: from=%d num=%d %s\n", from, num, (turn_on ? "on" : "off"));
+#endif
+#ifndef __arm__
+	set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on);
+#endif
+	return 0;
+}
+
+unsigned int *stack;
+
+/*
+ * sys_iopl has to be used when you want to access the IO ports
+ * beyond the 0x3ff range: to get the full 65536 ports bitmapped
+ * you'd need 8kB of bitmaps/process, which is a bit excessive.
+ *
+ * Here we just change the eflags value on the stack: we allow
+ * only the super-user to do it. This depends on the stack-layout
+ * on system-call entry - see also fork() and the signal handling
+ * code.
+ */
+asmlinkage int sys_iopl(long ebx,long ecx,long edx,
+	     long esi, long edi, long ebp, long eax, long ds,
+	     long es, long fs, long gs, long orig_eax,
+	     long eip,long cs,long eflags,long esp,long ss)
+{
+	unsigned int level = ebx;
+
+	if (level > 3)
+		return -EINVAL;
+	if (!suser())
+		return -EPERM;
+	*(&eflags) = (eflags & 0xffffcfff) | (level << 12);
+	return 0;
+}
diff -urNwbB linux/arch/arm/kernel/irq.c linux.arm/arch/arm/kernel/irq.c
--- linux/arch/arm/kernel/irq.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/irq.c	Sun Mar  3 13:04:56 1996
@@ -0,0 +1,360 @@
+/*
+ *  linux/arch/arm/kernel/irq.c
+ *
+ *  Copyright (C) 1992 Linus Torvalds
+ *  Modifications for ARM processor Copyright (C) 1995, 1996 Russell King.
+ *
+ * This file contains the code used by various IRQ handling routines:
+ * asking for different IRQ's should be done through these routines
+ * instead of just grabbing them. Thus setups with different IRQ numbers
+ * shouldn't result in any weird surprises, and installing new handlers
+ * should be easier.
+ */
+
+/*
+ * IRQ's are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+
+/*
+ * Unlike the PC, we allow the interrupt to disable itself
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/random.h>
+
+#include <asm/system.h>
+
+extern char *ioc;
+
+/*
+ * Slight change here - we now keep a bit array of currently executing
+ * interrupts.  This means that we can enable other interrupts from an
+ * interrupt, but not the currently executing ones.
+ *
+ * Basically, executing_irqs is maintained by the interrupt code for
+ *   our benefit, enabled_irqs we maintain for the benefit of the
+ *   interrupt code.
+ */
+unsigned long executing_irqs = 0;
+unsigned long enabled_irqs = 0;
+
+void disable_irq(unsigned int irq_nr)
+{
+    extern void ecard_disableirq (unsigned int);
+    unsigned long flags;
+
+    save_flags(flags);
+    cli();
+
+    enabled_irqs &= ~(1 << irq_nr);
+
+    if (irq_nr < 8)
+	ioc[0x18] &= ~(1 << irq_nr);
+    else
+    if (irq_nr < 16)
+	ioc[0x28] &= ~(1 << (irq_nr & 7));
+    else
+    if (irq_nr < 24)
+	ioc[0x38] &= ~(1 << (irq_nr & 7));
+    else
+        ecard_disableirq (irq_nr);
+
+    restore_flags(flags);
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+    extern void ecard_enableirq (unsigned int);
+    unsigned long flags;
+
+    save_flags(flags);
+    cli();
+
+    enabled_irqs |= 1 << irq_nr;
+
+    if ((executing_irqs & (1 << irq_nr)) == 0) {
+	if (irq_nr < 8)
+	    ioc[0x18] |= 1 << irq_nr;
+	else
+    	if (irq_nr < 16)
+	    ioc[0x28] |= 1 << (irq_nr & 7);
+	else
+	if (irq_nr < 24)
+	    ioc[0x38] |= (1 << (irq_nr & 7));
+    }
+    if (irq_nr >= 24)
+	ecard_enableirq (irq_nr);
+
+    restore_flags(flags);
+}
+
+/*
+ * This builds up the IRQ handler stubs using some ugly macros in irq.h
+ *
+ * These macros create the low-level assembly IRQ routines that do all
+ * the operations that are needed to keep the AT interrupt-controller
+ * happy. They are also written to be fast - and to disable interrupts
+ * as little as humanly possible.
+ *
+ * NOTE! These macros expand to three different handlers for each line: one
+ * complete handler that does all the fancy stuff (including signal handling),
+ * and one fast handler that is meant for simple IRQ's that want to be
+ * atomic. The specific handler is chosen depending on the SA_INTERRUPT
+ * flag when installing a handler. Finally, one "bad interrupt" handler, that
+ * is used when no handler is present.
+ */
+
+#undef BUILD_IRQ
+#define BUILD_IRQ(s,n,m) \
+	void IRQ##n##_interrupt(void); \
+	void fast_IRQ##n##_interrupt(void); \
+	void bad_IRQ##n##_interrupt(void);
+void bad_IRQ(void);
+
+BUILD_IRQ(FIRST,0,0x01)
+BUILD_IRQ(FIRST,1,0x02)
+BUILD_IRQ(FIRST,2,0x04)
+BUILD_IRQ(FIRST,3,0x08)
+BUILD_IRQ(FIRST,4,0x10)
+BUILD_IRQ(FIRST,5,0x20)
+BUILD_IRQ(FIRST,6,0x40)
+BUILD_IRQ(FIRST,7,0x80)
+BUILD_IRQ(SECOND,8,0x01)
+BUILD_IRQ(SECOND,9,0x02)
+BUILD_IRQ(SECOND,10,0x04)
+BUILD_IRQ(SECOND,11,0x08)
+BUILD_IRQ(SECOND,12,0x10)
+BUILD_IRQ(SECOND,13,0x20)
+BUILD_IRQ(SECOND,14,0x40)
+BUILD_IRQ(SECOND,15,0x80)
+
+/*
+ * Pointers to the low-level handlers: first the general ones, then the
+ * fast ones, then the bad ones.
+ */
+static void (*interrupt[16])(void) = {
+	IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt,
+	IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
+	IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
+	IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt
+};
+
+static void (*fast_interrupt[16])(void) = {
+	fast_IRQ0_interrupt, fast_IRQ1_interrupt,
+	fast_IRQ2_interrupt, fast_IRQ3_interrupt,
+	fast_IRQ4_interrupt, fast_IRQ5_interrupt,
+	fast_IRQ6_interrupt, fast_IRQ7_interrupt,
+	fast_IRQ8_interrupt, fast_IRQ9_interrupt,
+	fast_IRQ10_interrupt, fast_IRQ11_interrupt,
+	fast_IRQ12_interrupt, fast_IRQ13_interrupt,
+	fast_IRQ14_interrupt, fast_IRQ15_interrupt
+};
+
+static void (*bad_interrupt[16])(void) = {
+	bad_IRQ0_interrupt, bad_IRQ1_interrupt,
+	bad_IRQ2_interrupt, bad_IRQ3_interrupt,
+	bad_IRQ4_interrupt, bad_IRQ5_interrupt,
+	bad_IRQ6_interrupt, bad_IRQ7_interrupt,
+	bad_IRQ8_interrupt, bad_IRQ9_interrupt,
+	bad_IRQ10_interrupt, bad_IRQ11_interrupt,
+	bad_IRQ12_interrupt, bad_IRQ13_interrupt,
+	bad_IRQ14_interrupt, bad_IRQ15_interrupt
+};
+
+struct irqaction {
+	void (*handler)(int, struct pt_regs *);
+	unsigned long flags;
+	unsigned long mask;
+	const char *name;
+};
+
+/*
+ * Initial irq handlers.
+ */
+struct irqaction irq_action[32] = {
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }
+};
+
+void (*irqjump[32])(void) =
+{
+  bad_IRQ0_interrupt , bad_IRQ1_interrupt , bad_IRQ2_interrupt , bad_IRQ3_interrupt ,
+  bad_IRQ4_interrupt , bad_IRQ5_interrupt , bad_IRQ6_interrupt , bad_IRQ7_interrupt ,
+  bad_IRQ8_interrupt , bad_IRQ9_interrupt , bad_IRQ10_interrupt, bad_IRQ11_interrupt,
+  bad_IRQ12_interrupt, bad_IRQ13_interrupt, bad_IRQ14_interrupt, bad_IRQ15_interrupt,
+  bad_IRQ    , bad_IRQ    , bad_IRQ    , bad_IRQ,
+  bad_IRQ    , bad_IRQ    , bad_IRQ    , bad_IRQ,
+  bad_IRQ    , bad_IRQ    , bad_IRQ    , bad_IRQ,
+  bad_IRQ    , bad_IRQ    , bad_IRQ    , bad_IRQ,
+};
+
+int get_irq_list(char *buf)
+{
+	int i, len = 0;
+	struct irqaction * action = irq_action;
+
+	for (i = 0 ; i < 32 ; i++, action++) {
+		if (!action->handler)
+			continue;
+		len += sprintf(buf+len, "%2d: %8d %c %s\n",
+			i, kstat.interrupts[i],
+			(action->flags & SA_INTERRUPT) ? '+' : ' ',
+			action->name);
+	}
+	return len;
+}
+
+/*
+ * do_IRQ handles IRQ's that have been installed without the
+ * SA_INTERRUPT flag: it uses the full signal-handling return
+ * and runs with other interrupts enabled. All relatively slow
+ * IRQ's should use this format: notably the keyboard/timer
+ * routines.
+ */
+asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
+{
+	struct irqaction * action = irq + irq_action;
+
+	kstat.interrupts[irq]++;
+	if (action->flags & SA_SAMPLE_RANDOM)
+		add_interrupt_randomness(irq);
+	action->handler(irq, regs);
+}
+
+/*
+ * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return
+ * stuff - the handler is also running with interrupts disabled unless
+ * it explicitly enables them later.
+ */
+asmlinkage void do_fast_IRQ(int irq)
+{
+	struct irqaction * action = irq + irq_action;
+
+	kstat.interrupts[irq]++;
+	if (action->flags & SA_SAMPLE_RANDOM)
+		add_interrupt_randomness(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..
+ */
+int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+		 unsigned long irq_flags, const char * devname)
+{
+	struct irqaction *action;
+	unsigned long flags;
+	
+	if (irq > 32)
+		return -EINVAL;
+	action = irq_action + irq;
+	if(action->handler)
+		return -EBUSY;
+	if(!handler)
+		return -EINVAL;
+	
+	save_flags(flags);
+	cli();
+	action->handler = handler;
+	action->flags = irq_flags;
+	action->mask = 0;
+	action->name = devname;
+	if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
+		if (irq < 16) {
+			if (action->flags & SA_INTERRUPT)
+				irqjump[irq] = fast_interrupt[irq];
+			else
+				irqjump[irq] = interrupt[irq];
+		}
+		else
+			irqjump[irq] = (void (*)(void))handler;
+	}
+	enable_irq(irq);
+	restore_flags(flags);
+	return 0;
+}
+
+void free_irq(unsigned int irq)
+{
+	struct irqaction * action = irq + irq_action;
+	unsigned long flags;
+
+	if (irq > 32) {
+		printk("Trying to free IRQ%d\n",irq);
+		return;
+	}
+	if (!action->handler) {
+		printk("Trying to free free IRQ%d\n",irq);
+		return;
+	}
+	save_flags(flags);
+	cli();
+
+	disable_irq(irq);
+	if(irq < 16)
+		irqjump[irq]=bad_interrupt[irq];
+	else
+		irqjump[irq]=bad_IRQ;
+	action->handler = NULL;
+	action->flags = 0;
+	action->mask = 0;
+	action->name = NULL;
+	restore_flags(flags);
+}
+
+/*
+ * Note that on a 486, we don't want to do a SIGFPE on a irq13
+ * as the irq is unreliable, and exception 16 works correctly
+ * (ie as explained in the intel literature). On a 386, you
+ * can't use exception 16 due to bad IBM design, so we have to
+ * rely on the less exact irq13.
+ *
+ * Careful.. Not only is IRQ13 unreliable, but it is also
+ * leads to races. IBM designers who came up with it should
+ * be shot.
+ */
+
+unsigned long probe_irq_on (void)
+{
+	return 0;
+}
+
+int probe_irq_off (unsigned long irqs)
+{
+	return 0;
+}
+
+void init_IRQ(void)
+{
+	ioc[0x40]=LATCH & 255;
+	ioc[0x44]=LATCH>>8;
+	ioc[0x48]=0;
+	ioc[0x18]=0;
+	ioc[0x28]=0;
+	ioc[0x38]=0;
+}
diff -urNwbB linux/arch/arm/kernel/process.c linux.arm/arch/arm/kernel/process.c
--- linux/arch/arm/kernel/process.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/process.c	Thu Feb 22 13:45:02 1996
@@ -0,0 +1,176 @@
+/*
+ *  linux/arch/arm/kernel/process.c
+ *
+ *  Copyright (C) 1996 Russell King - Converted to ARM.
+ *  Origional Copyright (C) 1995  Linus Torvalds
+ */
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#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 <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+asmlinkage void ret_from_sys_call(void) __asm__("_ret_from_sys_call");
+
+static int hlt_counter=0;
+
+void disable_hlt(void)
+{
+    hlt_counter++;
+}
+
+void enable_hlt(void)
+{
+    hlt_counter--;
+}
+
+/*
+ * The idle loop on an arm..
+ */
+asmlinkage int sys_idle(void)
+{
+    if (current->pid != 0)
+	return -EPERM;
+
+    /* endless idle loop with no priority at all */
+    current->counter = -100;
+    for (;;)
+	schedule();
+}
+
+/* --------------------------------------------------------------------------------------- *
+ * Reset										   *
+ * --------------------------------------------------------------------------------------- */
+
+void hard_reset_now(void)
+{
+    /*
+     * If we are using the ARM3, turn off the cache!
+     * otherwise we get problems with 256 colour modes
+     * and floppy drive accesses in RiscOS.
+     */
+    extern unsigned long arm_id;
+    if ((arm_id & 0xfffffff0) == 0x41560300)
+	__asm__ __volatile__ ("mcr 15, 0, %0, c2, c0": : "r" (2));
+
+    /* Copy branch instr to reset location & call it */
+    *(unsigned long *)0 = *(unsigned long *)0x03800000;
+    ((void(*)(void))0)();
+}
+
+void show_regs(struct pt_regs * regs)
+{
+    printk("\n");
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+}
+
+void flush_thread(void)
+{
+    int i;
+    for (i=0 ; i<8 ; i++)
+	current->debugreg[i] = 0;
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+}
+
+void copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
+	struct task_struct * p, struct pt_regs * regs)
+{
+    struct pt_regs * childregs;
+
+    childregs = ((struct pt_regs *)(p->kernel_stack_page + 4096)) - 1;
+    p->tss.sregs[9] = (unsigned long) childregs;
+    p->tss.sregs[10] = (unsigned long) ret_from_sys_call | 3; /* force svc mode */
+    p->tss.fs = USER_DS;
+    *childregs = *regs;
+    childregs->ARM_r0 = 0;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+    int i;
+
+/* changed the size calculations - should hopefully work better. lbt */
+    dump->magic = CMAGIC;
+    dump->start_code = 0;
+    dump->start_stack = regs->ARM_sp & ~(PAGE_SIZE - 1);
+    dump->u_tsize = (((unsigned long) current->mm->end_code) - 32768) >> PAGE_SHIFT;
+    dump->u_dsize = (((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) - 32768) >> PAGE_SHIFT;
+    dump->u_dsize -= dump->u_tsize;
+    dump->u_ssize = 0;
+    for (i = 0; i < 8; i++)
+	dump->u_debugreg[i] = current->debugreg[i];  
+
+    if (dump->start_stack < TASK_SIZE)
+	dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
+
+    dump->regs = *regs;
+    dump->u_fpvalid = 0;
+}
+
+asmlinkage int sys_fork(void)
+{
+    struct pt_regs *regs;
+    __asm__("mov\t%0, r9\n\t": "=r" (regs) );
+
+    return do_fork(SIGCHLD, regs->ARM_sp, regs);
+}
+
+asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp)
+{
+    struct pt_regs *regs;
+    __asm__("mov\t%0, r9\n\t":"=r" (regs) );
+    if (!newsp)
+	newsp = regs->ARM_sp;
+
+    return do_fork(clone_flags, newsp, regs);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(char *filenamei, char **argv, char **envp)
+{
+    int error;
+    char * filename;
+    struct pt_regs *regs;
+    /*
+     * I hate this type of thing.  Oh well.
+     * I can't gaurantee any way of getting the registers efficiently
+     */
+    __asm__("mov\t%0, r9\n\t": "=r" (regs));
+
+    error = getname(filenamei, &filename);
+    if (error)
+	return error;
+    error = do_execve(filename, argv, envp, regs);
+    putname(filename);
+    return error;
+}
diff -urNwbB linux/arch/arm/kernel/ptrace.c linux.arm/arch/arm/kernel/ptrace.c
--- linux/arch/arm/kernel/ptrace.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/ptrace.c	Mon Feb 19 22:12:02 1996
@@ -0,0 +1,531 @@
+/* ptrace.c */
+/* By Ross Biro 1/23/92 */
+/* edited by Linus Torvalds */
+/* edited for ARM by Russell King */
+
+#include <linux/config.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/* determines which flags the user has access to. */
+/* 1 = access 0 = no access */
+#define FLAG_MASK 0x00044dd5
+
+/* set's the trap flag. */
+#define TRAP_FLAG 0x100
+
+/*
+ * this is the number to subtract from the top of the stack. To find
+ * the local frame.
+ */
+#define MAGICNUMBER 68
+
+/* change a pid into a task struct. */
+static inline struct task_struct * get_task(int pid)
+{
+	int i;
+
+	for (i = 1; i < NR_TASKS; i++) {
+		if (task[i] != NULL && (task[i]->pid == pid))
+			return task[i];
+	}
+	return NULL;
+}
+
+/*
+ * this routine will get a word off of the processes privileged stack.
+ * the offset is how far from the base addr as stored in the TSS.
+ * this routine assumes that all the privileged stacks are in our
+ * data space.
+ */
+static inline long get_stack_long(struct task_struct *task, int offset)
+{
+	unsigned char *stack;
+
+	stack = (unsigned char *)(task->kernel_stack_page + 4096 - 17*4);
+	stack += offset*4;
+	return (*((long *)stack));
+}
+
+/*
+ * this routine will put a word on the processes privileged stack.
+ * the offset is how far from the base addr as stored in the TSS.
+ * this routine assumes that all the privileged stacks are in our
+ * data space.
+ */
+static inline long put_stack_long(struct task_struct *task, int offset,
+	unsigned long data)
+{
+	unsigned char *stack;
+
+	stack = (unsigned char *)(task->kernel_stack_page + 4096 - 17*4);
+	stack += offset*4;
+	*(unsigned long *) stack = data;
+	return 0;
+}
+
+/*
+ * This routine gets a long from any process space by following the page
+ * tables. NOTE! You should check that the long isn't on a page boundary,
+ * and that it is in the task area before calling this: this routine does
+ * no checking.
+ */
+static unsigned long get_long(struct task_struct * tsk,
+	struct vm_area_struct * vma, unsigned long addr)
+{
+	pgd_t *pgdir;
+	pmd_t *pgmiddle;
+	pte_t *pgtable;
+	unsigned long page;
+
+repeat:
+	pgdir = pgd_offset(vma->vm_mm, addr);
+	if (pgd_none(*pgdir)) {
+		do_no_page(tsk, vma, addr, 0);
+		goto repeat;
+	}
+	if (pgd_bad(*pgdir)) {
+		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
+		pgd_clear(pgdir);
+		return 0;
+	}
+	pgmiddle = pmd_offset(pgdir, addr);
+	if (pmd_none(*pgmiddle)) {
+		do_no_page(tsk, vma, addr, 0);
+		goto repeat;
+	}
+	if (pmd_bad(*pgmiddle)) {
+		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
+		pmd_clear(pgmiddle);
+		return 0;
+	}
+	pgtable = pte_offset(pgmiddle, addr);
+	if (!pte_present(*pgtable)) {
+		do_no_page(tsk, vma, addr, 0);
+		goto repeat;
+	}
+	page = pte_page(*pgtable);
+ 
+	if(page >= high_memory)
+		return 0;
+	page += addr & ~PAGE_MASK;
+	return *(unsigned long *)page;
+}
+
+/*
+ * This routine puts a long into any process space by following the page
+ * tables. NOTE! You should check that the long isn't on a page boundary,
+ * and that it is in the task area before calling this: this routine does
+ * no checking.
+ *
+ * Now keeps R/W state of the page so that a text page stays readonly
+ * even if a debugger scribbles breakpoints into it.  -M.U-
+ */
+static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr,
+	unsigned long data)
+{
+	pgd_t *pgdir;
+	pmd_t *pgmiddle;
+	pte_t *pgtable;
+	unsigned long page;
+
+repeat:
+	pgdir = pgd_offset(vma->vm_mm, addr);
+	if (!pgd_present(*pgdir)) {
+		do_no_page(tsk, vma, addr, 1);
+		goto repeat;
+	}
+	if (pgd_bad(*pgdir)) {
+		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
+		pgd_clear(pgdir);
+		return;
+	}
+	pgmiddle = pmd_offset(pgdir, addr);
+	if (pmd_none(*pgmiddle)) {
+		do_no_page(tsk, vma, addr, 1);
+		goto repeat;
+	}
+	if (pmd_bad(*pgmiddle)) {
+		printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
+		pmd_clear(pgmiddle);
+		return;
+	}
+	pgtable = pte_offset(pgmiddle, addr);
+	if (!pte_present(*pgtable)) {
+		do_no_page(tsk, vma, addr, 1);
+		goto repeat;
+	}
+	page = pte_page(*pgtable);
+	if (!pte_write(*pgtable)) {
+		do_wp_page(tsk, vma, addr, 1);
+		goto repeat;
+	}
+	
+	if (page < high_memory)
+		*(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
+	set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+	invalidate();
+}
+
+static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
+{
+	struct vm_area_struct * vma;
+
+	addr &= PAGE_MASK;
+	vma = find_vma(tsk,addr);
+	if (!vma)
+		return NULL;
+	if (vma->vm_start <= addr)
+		return vma;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		return NULL;
+	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
+		return NULL;
+	vma->vm_offset -= vma->vm_start - addr;
+	vma->vm_start = addr;
+	return vma;
+}
+
+/*
+ * This routine checks the page boundaries, and that the offset is
+ * within the task area. It then calls get_long() to read a long.
+ */
+static int read_long(struct task_struct * tsk, unsigned long addr,
+	unsigned long * result)
+{
+	struct vm_area_struct * vma = find_extend_vma(tsk, addr);
+
+	if (!vma)
+		return -EIO;
+	if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
+		unsigned long low,high;
+		struct vm_area_struct * vma_high = vma;
+
+		if (addr + sizeof(long) >= vma->vm_end) {
+			vma_high = vma->vm_next;
+			if (!vma_high || vma_high->vm_start != vma->vm_end)
+				return -EIO;
+		}
+		low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
+		high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
+		switch (addr & (sizeof(long)-1)) {
+			case 1:
+				low >>= 8;
+				low |= high << 24;
+				break;
+			case 2:
+				low >>= 16;
+				low |= high << 16;
+				break;
+			case 3:
+				low >>= 24;
+				low |= high << 8;
+				break;
+		}
+		*result = low;
+	} else
+		*result = get_long(tsk, vma, addr);
+	return 0;
+}
+
+/*
+ * This routine checks the page boundaries, and that the offset is
+ * within the task area. It then calls put_long() to write a long.
+ */
+static int write_long(struct task_struct * tsk, unsigned long addr,
+	unsigned long data)
+{
+	struct vm_area_struct * vma = find_extend_vma(tsk, addr);
+
+	if (!vma)
+		return -EIO;
+	if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
+		unsigned long low,high;
+		struct vm_area_struct * vma_high = vma;
+
+		if (addr + sizeof(long) >= vma->vm_end) {
+			vma_high = vma->vm_next;
+			if (!vma_high || vma_high->vm_start != vma->vm_end)
+				return -EIO;
+		}
+		low = get_long(tsk, vma, addr & ~(sizeof(long)-1));
+		high = get_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
+		switch (addr & (sizeof(long)-1)) {
+			case 0: /* shouldn't happen, but safety first */
+				low = data;
+				break;
+			case 1:
+				low &= 0x000000ff;
+				low |= data << 8;
+				high &= ~0xff;
+				high |= data >> 24;
+				break;
+			case 2:
+				low &= 0x0000ffff;
+				low |= data << 16;
+				high &= ~0xffff;
+				high |= data >> 16;
+				break;
+			case 3:
+				low &= 0x00ffffff;
+				low |= data << 24;
+				high &= ~0xffffff;
+				high |= data >> 8;
+				break;
+		}
+		put_long(tsk, vma, addr & ~(sizeof(long)-1),low);
+		put_long(tsk, vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
+	} else
+		put_long(tsk, vma, addr, data);
+	return 0;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+	struct task_struct *child;
+	struct user * dummy;
+	int i;
+
+	dummy = NULL;
+
+	if (request == PTRACE_TRACEME) {
+		/* are we already being traced? */
+		if (current->flags & PF_PTRACED)
+			return -EPERM;
+		/* set the ptrace bit in the process flags. */
+		current->flags |= PF_PTRACED;
+		return 0;
+	}
+	if (pid == 1)		/* you may not mess with init */
+		return -EPERM;
+	if (!(child = get_task(pid)))
+		return -ESRCH;
+	if (request == PTRACE_ATTACH) {
+		if (child == current)
+			return -EPERM;
+		if ((!child->dumpable ||
+		    (current->uid != child->euid) ||
+		    (current->uid != child->uid) ||
+	 	    (current->gid != child->egid) ||
+	 	    (current->gid != child->gid)) && !suser())
+			return -EPERM;
+		/* the same process cannot be attached many times */
+		if (child->flags & PF_PTRACED)
+			return -EPERM;
+		child->flags |= PF_PTRACED;
+		if (child->p_pptr != current) {
+			REMOVE_LINKS(child);
+			child->p_pptr = current;
+			SET_LINKS(child);
+		}
+		send_sig(SIGSTOP, child, 1);
+		return 0;
+	}
+	if (!(child->flags & PF_PTRACED))
+		return -ESRCH;
+	if (child->state != TASK_STOPPED) {
+		if (request != PTRACE_KILL)
+			return -ESRCH;
+	}
+	if (child->p_pptr != current)
+		return -ESRCH;
+
+	switch (request) {
+	/* when I and D space are separate, these will need to be fixed. */
+		case PTRACE_PEEKTEXT: /* read word at location addr. */
+		case PTRACE_PEEKDATA: {
+			unsigned long tmp;
+			int res;
+
+			res = read_long(child, addr, &tmp);
+			if (res < 0)
+				return res;
+			res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
+			if (!res)
+				put_fs_long(tmp,(unsigned long *) data);
+			return res;
+		}
+
+	/* read the word at location addr in the USER area. */
+		case PTRACE_PEEKUSR: {
+			unsigned long tmp;
+			int res;
+
+			if ((addr & 3) || addr < 0 ||
+			    addr > sizeof(struct user) - 3)
+				return -EIO;
+
+			res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
+			if (res)
+				return res;
+			tmp = 0;  /* Default return condition */
+			if(addr < 17*sizeof(long)) {
+			  addr = addr >> 2; /* temporary hack. */
+
+			  tmp = get_stack_long(child, (int)addr);
+			}
+			if(addr >= (long) &dummy->u_debugreg[0] &&
+			   addr <= (long) &dummy->u_debugreg[7]){
+				addr -= (long) &dummy->u_debugreg[0];
+				addr = addr >> 2;
+				tmp = child->debugreg[addr];
+			}
+			put_fs_long(tmp,(unsigned long *) data);
+			return 0;
+		}
+
+      /* when I and D space are separate, this will have to be fixed. */
+		case PTRACE_POKETEXT: /* write the word at location addr. */
+		case PTRACE_POKEDATA:
+			return write_long(child,addr,data);
+#if 0
+		case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+			if ((addr & 3) || addr < 0 ||
+			    addr > sizeof(struct user) - 3)
+				return -EIO;
+
+			addr = addr >> 2; /* temporary hack. */
+
+			if (addr == ORIG_EAX)
+				return -EIO;
+			if (addr == DS || addr == ES ||
+			    addr == FS || addr == GS ||
+			    addr == CS || addr == SS) {
+			    	data &= 0xffff;
+			    	if (data && (data & 3) != 3)
+					return -EIO;
+			}
+			if (addr == EFL) {   /* flags. */
+				data &= FLAG_MASK;
+				data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER)  & ~FLAG_MASK;
+			}
+		  /* Do not allow the user to set the debug register for kernel
+		     address space */
+		  if(addr < 17){
+			  if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))
+				return -EIO;
+			return 0;
+			};
+
+		  /* We need to be very careful here.  We implicitly
+		     want to modify a portion of the task_struct, and we
+		     have to be selective about what portions we allow someone
+		     to modify. */
+
+		  addr = addr << 2;  /* Convert back again */
+		  if(addr >= (long) &dummy->u_debugreg[0] &&
+		     addr <= (long) &dummy->u_debugreg[7]){
+
+			  if(addr == (long) &dummy->u_debugreg[4]) return -EIO;
+			  if(addr == (long) &dummy->u_debugreg[5]) return -EIO;
+			  if(addr < (long) &dummy->u_debugreg[4] &&
+			     ((unsigned long) data) >= 0xbffffffd) return -EIO;
+
+			  if(addr == (long) &dummy->u_debugreg[7]) {
+				  data &= ~DR_CONTROL_RESERVED;
+				  for(i=0; i<4; i++)
+					  if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
+						  return -EIO;
+			  };
+
+			  addr -= (long) &dummy->u_debugreg;
+			  addr = addr >> 2;
+			  child->debugreg[addr] = data;
+			  return 0;
+		  };
+		  return -EIO;
+#endif
+		case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+		case PTRACE_CONT: { /* restart after signal. */
+
+			if ((unsigned long) data > NSIG)
+				return -EIO;
+			if (request == PTRACE_SYSCALL)
+				child->flags |= PF_TRACESYS;
+			else
+				child->flags &= ~PF_TRACESYS;
+			child->exit_code = data;
+			wake_up_process (child);
+			return 0;
+		}
+
+/*
+ * make the child exit.  Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+		case PTRACE_KILL: {
+/*			long tmp;*/
+
+			if (child->state == TASK_ZOMBIE)	/* already dead */
+				return 0;
+			wake_up_process (child);
+			child->exit_code = SIGKILL;
+			return 0;
+		}
+#if 0
+		case PTRACE_SINGLESTEP: {  /* set the trap flag. */
+			long tmp;
+
+			if ((unsigned long) data > NSIG)
+				return -EIO;
+			child->flags &= ~PF_TRACESYS;
+			tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;
+			put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
+			child->state = TASK_RUNNING;
+			child->exit_code = data;
+	/* give it a chance to run. */
+			return 0;
+		}
+#endif
+		case PTRACE_DETACH: { /* detach a process that was attached. */
+/*			long tmp;*/
+
+			if ((unsigned long) data > NSIG)
+				return -EIO;
+			child->flags &= ~(PF_PTRACED|PF_TRACESYS);
+			wake_up_process (child);
+			child->exit_code = data;
+			REMOVE_LINKS(child);
+			child->p_pptr = child->p_opptr;
+			SET_LINKS(child);
+			return 0;
+		}
+
+		default:
+			return -EIO;
+	}
+}
+
+asmlinkage void syscall_trace(void)
+{
+	if ((current->flags & (PF_PTRACED|PF_TRACESYS))
+			!= (PF_PTRACED|PF_TRACESYS))
+		return;
+	current->exit_code = SIGTRAP;
+	current->state = TASK_STOPPED;
+	notify_parent(current);
+	schedule();
+	/*
+	 * this isn't the same as continuing with a signal, but it will do
+	 * for normal use.  strace only continues with a signal if the
+	 * stopping signal is not SIGTRAP.  -brl
+	 */
+	if (current->exit_code)
+		current->signal |= (1 << (current->exit_code - 1));
+	current->exit_code = 0;
+}
diff -urNwbB linux/arch/arm/kernel/setup.c linux.arm/arch/arm/kernel/setup.c
--- linux/arch/arm/kernel/setup.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/setup.c	Sun Mar  3 13:06:00 1996
@@ -0,0 +1,235 @@
+/*
+ *  linux/arch/arm/kernel/setup.c
+ *
+ *  Copyright (C) 1995, 1996 Russell King
+ */
+
+/*
+ * This file obtains various parameters about the system that the kernel
+ * is running on.
+ */
+
+#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 <linux/ioport.h>
+#include <linux/delay.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#define F_MEMC   (1<<0)
+#define F_MMU    (1<<1)
+#define F_32BIT  (1<<2)
+#define F_IOEB   (1<<31)
+
+struct drive_info_struct { char dummy[32]; } drive_info;
+
+struct screen_info screen_info = {
+	0, 0,
+	{ 0, 0 },
+	0,
+	0,
+	100,
+	0, 0, 0,
+	75
+};
+
+struct param_struct {
+	unsigned long page_size;
+	unsigned long nr_pages;
+	unsigned long ramdisk_size;
+	unsigned long mountrootrdonly;
+	unsigned long rootdev;
+	unsigned long video_num_cols;
+	unsigned long video_num_rows;
+	unsigned long video_x;
+	unsigned long video_y;
+	unsigned long memc_control_reg;
+	unsigned char sounddefault;
+	unsigned char adfsdrives;
+	unsigned char bytes_per_char_h;
+	unsigned char bytes_per_char_v;
+	unsigned long unused[256/4-11];
+	char paths[8][128];
+	char commandline[256];
+} *params = (struct param_struct *)0x0207C000;
+
+struct armversions {
+	unsigned long id;
+	unsigned long mask;
+	unsigned long features;
+	unsigned long io_base;
+	const char *manu;
+	const char *name;
+} armidlist[] = {
+	{ 0x41560200, 0xfffffff0, F_MEMC	, 0x03000000	, "ARM",	"arm2"		},
+	{ 0x41560250, 0xfffffff0, F_MEMC	, 0x03000000	, "ARM",	"arm250"	},
+	{ 0x41560300, 0xfffffff0, F_MEMC	, 0x03000000	, "ARM",	"arm3"		},
+	{ 0x41560610, 0xfffffff0, F_MMU|F_32BIT	, 0		, "ARM",	"arm610"	},
+	{ 0x44450000, 0xffff0000, F_MMU		, 0		, "DEC",	"SA-1"		},
+	{ 0x00000000, 0x00000000, 0		, 0		, "***",	"*unknown*"	}
+};
+
+#define ISSET(bit) (armidlist[armidindex].features & bit)
+
+#define COMMAND_LINE_SIZE 256
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+
+unsigned char aux_device_present;
+unsigned char *ioc;
+unsigned long io_base;
+unsigned long arm_id;
+int armidindex;
+int ioebpresent;
+int memc_ctrl_reg;
+
+extern int bytes_per_char_h;
+extern int bytes_per_char_v;
+extern char arc_hd_files[8][128];
+extern int no_hds;
+extern int ramdisk_size;
+extern int root_mountflags;
+extern int _etext, _edata, _end;
+extern char *empty_zero_page;
+
+static void check_ioeb_present(void)
+{
+	if (((*(unsigned char *)(io_base + 0x350050)) & 15) == 5)
+		armidlist[armidindex].features |= F_IOEB;
+}
+
+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;
+	int len = 0, i;
+
+	armidindex = 0;
+	while (1) {
+		if (!((armidlist[armidindex].id ^ arm_id) &
+			armidlist[armidindex].mask))
+			break;
+		armidindex += 1;
+	}
+
+#ifndef __arm6__
+	if (!(armidlist[armidindex].features & F_MEMC)) {
+		*(unsigned long *)0x02000000 = 0x11111111;
+		while (1);
+	}
+#else
+	if (!(armidlist[armidindex].features & F_MMU)) {
+		*(unsigned long *)0x02000000 = 0x22222222;
+		while (1);
+	}
+#endif
+
+	io_base = armidlist[armidindex].io_base;
+	ioc = (unsigned char *)(io_base + 0x200000);
+
+	check_ioeb_present();
+
+	if ((arm_id & 0x0000fff0) == 0x300) {
+		__asm__ __volatile__("
+		mcr	15, 0, %0, c3, c0
+		mcr	15, 0, %1, c4, c0
+		mcr	15, 0, %2, c5, c0
+		mcr	15, 0, %3, c1, c0
+		mcr	15, 0, %3, c2, c0
+		" : : "r" (0x000e7fff), "r" (0x000e7fff), "r" (0xf0000000), "r" (3));
+	} else {
+		/* ok, so this is a bit dirty... Self modifing code! */
+		extern unsigned long *arm3_flushcache;
+		arm3_flushcache[0] = arm3_flushcache[1];
+	}
+
+	memory_start = (unsigned long)&_end - 0x01800000+0x02080000;
+
+	bytes_per_char_h = params->bytes_per_char_h;
+	bytes_per_char_v = params->bytes_per_char_v;
+	from = params->commandline;
+	ROOT_DEV = params->rootdev;
+	ORIG_X = params->video_x;
+	ORIG_Y = params->video_y;
+	ORIG_VIDEO_COLS = params->video_num_cols;
+	ORIG_VIDEO_LINES = params->video_num_rows;
+	memc_ctrl_reg = params->memc_control_reg;
+	no_hds = (params->adfsdrives >> 6) & 3;
+
+	aux_device_present = 0;
+	memory_end = 0x02000000 + params->page_size * params->nr_pages;
+	ramdisk_size = params->ramdisk_size;
+
+	if (params->mountrootrdonly)
+		root_mountflags |= MS_RDONLY;
+
+	for(i = 0; i < 8; i++)
+		strcpy(arc_hd_files[i], params->paths[i]);
+
+	init_task.mm->start_code = TASK_SIZE;
+	init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
+	init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata;
+	init_task.mm->brk = TASK_SIZE + (unsigned long) &_end;
+
+	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++;
+			}
+			memory_end = memory_end + 0x02000000;
+		}
+		c = *(from++);
+		if (!c)
+			break;
+		if (COMMAND_LINE_SIZE <= ++len)
+			break;
+		*(to++) = c;
+	}
+	*to = '\0';
+	*cmdline_p = command_line;
+	*memory_start_p = memory_start;
+	*memory_end_p = memory_end;
+}
+
+int get_cpuinfo(char * buffer)
+{
+	int len;
+
+	len = sprintf (buffer,  "CPU:\n"
+				"Type\t\t: %s\n"
+				"Revision\t: %d\n"
+				"Manufacturer\t: %s\n"
+				"32bit modes\t: %s\n"
+				"BogoMips\t: %lu.%02lu\n",
+				armidlist[armidindex].name,
+				(int)arm_id & 15,
+				armidlist[armidindex].manu,
+				ISSET (F_32BIT) ? "yes" : "no",
+				loops_per_sec / 500000,
+				(loops_per_sec / 5000) % 100);
+	len += sprintf (buffer + len,
+				"\nHardware:\n"
+				"Mem System\t: %s\n"
+				"IOEB\t\t: %s\n",
+				ISSET(F_MEMC)  ? "MEMC" : 
+				ISSET(F_MMU)   ? "MMU"  : "*unknown*",
+				ISSET(F_IOEB)  ? "present" : "absent"
+				);
+	return len;
+}
diff -urNwbB linux/arch/arm/kernel/signal.c linux.arm/arch/arm/kernel/signal.c
--- linux/arch/arm/kernel/signal.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/signal.c	Sun Mar  3 13:06:11 1996
@@ -0,0 +1,254 @@
+/*
+ *  linux/arch/arm/kernel/signal.c
+ *
+ *  Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+
+#include <asm/segment.h>
+
+#define _S(nr) (1<<((nr)-1))
+
+#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+
+#define SWI_SYS_SIGRETURN 0xef900077
+
+asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs);
+
+/*
+ * atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
+{
+    unsigned long mask;
+    struct pt_regs * regs;
+    __asm__("mov\t%0,r9\n\t": "=r" (regs));
+
+    mask = current->blocked;
+    current->blocked = set & _BLOCKABLE;
+    regs->ARM_r0 = -EINTR;
+
+    while (1) {
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	if (do_signal(mask,regs))
+	    return regs->ARM_r0;
+    }
+}
+
+/*
+ * This sets regs->esp even though we don't actually use sigstacks yet..
+ */
+asmlinkage int sys_sigreturn(unsigned long __unused)
+{
+    struct pt_regs *regs;
+    struct sigcontext_struct context;
+    void *frame;
+    int i;
+  	
+    __asm__ ("mov\t%0, r9\n\t": "=r" (regs));
+
+    frame = (void *)regs->ARM_sp;
+
+    if (verify_area(VERIFY_READ,frame,sizeof(context))) {
+	printk("*** Bad stack frame\n");
+	goto badframe;
+    }
+
+    memcpy_fromfs(&context,frame,sizeof(context));
+
+    if (context.magic!=0x4B534154) {
+	printk("Bad stack identifier\n");
+	goto badframe;
+    }
+
+    for (i = 0; i < 17; i++)
+	regs->uregs[i]=context.reg[i];
+
+    current->blocked = context.oldmask & _BLOCKABLE;
+
+    return regs->ARM_r0;
+badframe:
+    do_exit(SIGSEGV);
+    return 0;
+}
+
+/*
+ * Set up a signal frame...
+ */
+static void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip,
+	struct pt_regs * regs, int signr, unsigned long oldmask)
+{
+    unsigned long * frame;
+    struct sigcontext_struct context;
+    int i;
+	
+    frame = (unsigned long *)(((int)(*fp)) - sizeof(context) - 4);
+
+    if (verify_area (VERIFY_WRITE, frame, sizeof(context)))
+	do_exit (SIGSEGV);
+
+    context.magic = 0x4B534154; /* Signature */
+    for (i = 0; i < 17; i++)
+	context.reg[i] = regs->uregs[i];
+    context.reg[13]	= (unsigned long)*fp;
+    context.reg[15]	= eip;
+
+    context.trap_no	= current->tss.trap_no;
+    context.error_code	= current->tss.error_code;
+    context.oldmask	= oldmask;
+
+    memcpy_tofs (frame, &context, sizeof(context));
+    put_user (SWI_SYS_SIGRETURN, frame + sizeof (context)/4);
+
+    *fp = frame;
+    if (current->exec_domain && current->exec_domain->signal_invmap)
+	regs->ARM_r0 = current->exec_domain->signal_invmap[signr];
+    else
+	regs->ARM_r0 = signr;
+    regs->ARM_lr = ((unsigned long)frame) + sizeof (context);
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
+{
+    unsigned long mask = ~current->blocked;
+    unsigned long handler_signal = 0;
+    unsigned long *frame = NULL;
+    unsigned long eip = 0;
+    unsigned long signr;
+    struct sigaction * sa;
+    unsigned long *pcinstr = (unsigned long *)((regs->ARM_pc & 0x03fffffc)-4);
+    int swiinstr;
+
+    if (verify_area(VERIFY_READ, pcinstr, 4) == 0 &&
+		(get_user(pcinstr) & 0x0f000000) == 0x0f000000)
+	swiinstr = 1;
+    else
+	swiinstr = 0;
+
+    while ((signr = current->signal & mask)) {
+	eip = signr;
+	for(signr=0; signr < 32; signr++)
+	    if(eip & (1 << signr)) {
+		current->signal &= ~(1 << signr);
+		break;
+	    }
+	sa = current->sig->action + signr;
+	signr++;
+	if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
+	    current->exit_code = signr;
+	    current->state = TASK_STOPPED;
+	    notify_parent(current);
+	    schedule();
+	    if (!(signr = current->exit_code))
+		continue;
+	    current->exit_code = 0;
+	    if (signr == SIGSTOP)
+		continue;
+	    if (_S(signr) & current->blocked) {
+		current->signal |= _S(signr);
+		continue;
+	    }
+	    sa = current->sig->action + signr - 1;
+	}
+	if (sa->sa_handler == SIG_IGN) {
+	    if (signr != SIGCHLD)
+		continue;
+	    /* check for SIGCHLD: it's special */
+	    while (sys_waitpid(-1,NULL,WNOHANG) > 0)
+		/* nothing */;
+	    continue;
+	}
+	if (sa->sa_handler == SIG_DFL) {
+	    if (current->pid == 1)
+		continue;
+	    switch (signr) {
+		case SIGCONT: case SIGCHLD: case SIGWINCH:
+		    continue;
+
+		case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+		    if (current->flags & PF_PTRACED)
+			continue;
+		    current->state = TASK_STOPPED;
+		    current->exit_code = signr;
+		    if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags &
+			    SA_NOCLDSTOP))
+			notify_parent(current);
+			schedule();
+			continue;
+
+		case SIGBUS:
+		case SIGQUIT: case SIGILL: case SIGTRAP:
+		case SIGIOT: case SIGFPE: case SIGSEGV:
+		    if (current->binfmt && current->binfmt->core_dump) {
+			if (current->binfmt->core_dump(signr, regs))
+			    signr |= 0x80;
+		    }
+				/* fall through */
+		default:
+		    current->signal |= _S(signr & 0x7f);
+		    do_exit(signr);
+	    }
+	}
+	/*
+	 * OK, we're invoking a handler
+	 */
+	if (swiinstr) {
+	    if (regs->ARM_r0 == -ERESTARTNOHAND ||
+		    (regs->ARM_r0 == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
+		regs->ARM_r0 = -EINTR;
+	}
+	handler_signal |= 1 << (signr-1);
+	mask &= ~sa->sa_mask;
+    }
+    if (swiinstr &&
+	    (regs->ARM_r0 == -ERESTARTNOHAND ||
+	     regs->ARM_r0 == -ERESTARTSYS ||
+	     regs->ARM_r0 == -ERESTARTNOINTR)) {
+		regs->ARM_r0 = regs->ARM_ORIG_r0;
+		regs->ARM_pc -= 4;
+    }
+    if (!handler_signal)		/* no handler will be called - return 0 */
+	return 0;
+
+    eip = regs->ARM_pc;
+    frame = (unsigned long *) regs->ARM_sp;
+    signr = 1;
+    sa = current->sig->action;
+    for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
+	if (mask > handler_signal)
+	    break;
+	if (!(mask & handler_signal))
+	    continue;
+	setup_frame(sa,&frame,eip,regs,signr,oldmask);
+	eip = (unsigned long) sa->sa_handler;
+	if (sa->sa_flags & SA_ONESHOT)
+	    sa->sa_handler = NULL;
+/* force a supervisor-mode page-in of the signal handler to reduce races */
+
+	    current->blocked |= sa->sa_mask;
+	    oldmask |= sa->sa_mask;
+    }
+    regs->ARM_sp = (unsigned long) frame;
+    regs->ARM_pc = eip;		/* "return" to the first handler */
+    current->tss.trap_no = current->tss.error_code = 0;
+    return 1;
+}
diff -urNwbB linux/arch/arm/kernel/sys-arm.c linux.arm/arch/arm/kernel/sys-arm.c
--- linux/arch/arm/kernel/sys-arm.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/sys-arm.c	Sun Mar  3 13:07:31 1996
@@ -0,0 +1,200 @@
+/*
+ * linux/arch/arm/kernel/sys-arm.c
+ *
+ * Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
+ * Copyright (C) 1995, 1996 Russell King.
+ * 
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/arm
+ * platform.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+
+#include <asm/segment.h>
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way unix tranditionally does this, though.
+ */
+asmlinkage int sys_pipe(unsigned long * fildes)
+{
+	int fd[2];
+	int error;
+
+	error = verify_area(VERIFY_WRITE,fildes,8);
+	if (error)
+		return error;
+	error = do_pipe(fd);
+	if (error)
+		return error;
+	put_fs_long(fd[0],0+fildes);
+	put_fs_long(fd[1],1+fildes);
+	return 0;
+}
+
+/*
+ * Perform the select(nd, in, out, ex, tv) and mmap() system
+ * calls. Linux/i386 didn't use to be able to handle more than
+ * 4 system call parameters, so these system calls used a memory
+ * block for parameter passing..
+ */
+asmlinkage int old_mmap(unsigned long *buffer)
+{
+	int error;
+	unsigned long flags;
+	struct file * file = NULL;
+
+	error = verify_area(VERIFY_READ, buffer, 6*sizeof(long));
+	if (error)
+		return error;
+	flags = get_user(buffer+3);
+	if (!(flags & MAP_ANONYMOUS)) {
+		unsigned long fd = get_user(buffer+4);
+		if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+			return -EBADF;
+	}
+	return do_mmap(file, get_user(buffer), get_user(buffer+1),
+		       get_user(buffer+2), flags, get_user(buffer+5));
+}
+
+
+extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+
+asmlinkage int old_select(unsigned long *buffer)
+{
+	int n;
+	fd_set *inp;
+	fd_set *outp;
+	fd_set *exp;
+	struct timeval *tvp;
+
+	n = verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long));
+	if (n)
+		return n;
+	n = get_user(buffer);
+	inp = (fd_set *) get_user(buffer+1);
+	outp = (fd_set *) get_user(buffer+2);
+	exp = (fd_set *) get_user(buffer+3);
+	tvp = (struct timeval *) get_user(buffer+4);
+	return sys_select(n, inp, outp, exp, tvp);
+}
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
+{
+	int version;
+
+	version = call >> 16; /* hack for backward compatibility */
+	call &= 0xffff;
+
+	if (call <= SEMCTL)
+		switch (call) {
+		case SEMOP:
+			return sys_semop (first, (struct sembuf *)ptr, second);
+		case SEMGET:
+			return sys_semget (first, second, third);
+		case SEMCTL: {
+			union semun fourth;
+			int err;
+			if (!ptr)
+				return -EINVAL;
+			if ((err = verify_area (VERIFY_READ, ptr, sizeof(long))))
+				return err;
+			fourth.__pad = (void *) get_fs_long(ptr);
+			return sys_semctl (first, second, third, fourth);
+			}
+		default:
+			return -EINVAL;
+		}
+	if (call <= MSGCTL) 
+		switch (call) {
+		case MSGSND:
+			return sys_msgsnd (first, (struct msgbuf *) ptr, 
+					   second, third);
+		case MSGRCV:
+			switch (version) {
+			case 0: {
+				struct ipc_kludge tmp;
+				int err;
+				if (!ptr)
+					return -EINVAL;
+				if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
+					return err;
+				memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
+					       sizeof (tmp));
+				return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
+				}
+			case 1: default:
+				return sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third);
+			}
+		case MSGGET:
+			return sys_msgget ((key_t) first, second);
+		case MSGCTL:
+			return sys_msgctl (first, second, (struct msqid_ds *) ptr);
+		default:
+			return -EINVAL;
+		}
+	if (call <= SHMCTL) 
+		switch (call) {
+		case SHMAT:
+			switch (version) {
+			case 0: default: {
+				ulong raddr;
+				int err;
+				if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong))))
+					return err;
+				err = sys_shmat (first, (char *) ptr, second, &raddr);
+				if (err)
+					return err;
+				put_fs_long (raddr, (ulong *) third);
+				return 0;
+				}
+			case 1:	/* iBCS2 emulator entry point */
+				if (get_fs() != get_ds())
+					return -EINVAL;
+				return sys_shmat (first, (char *) ptr, second, (ulong *) third);
+			}
+		case SHMDT: 
+			return sys_shmdt ((char *)ptr);
+		case SHMGET:
+			return sys_shmget (first, second, third);
+		case SHMCTL:
+			return sys_shmctl (first, second, (struct shmid_ds *) ptr);
+		default:
+			return -EINVAL;
+		}
+	return -EINVAL;
+}
+
+asmlinkage int
+sys_arm_init_module (char *module_name, char *code, unsigned codesize, unsigned long *__p)
+{
+    return sys_init_module (module_name, code, codesize, (struct mod_routines *)__p[0],
+    		(struct symbol_table *)__p[1]);
+}
+
+asmlinkage int
+sys_arm_llseek (unsigned int fd, unsigned long offset_high, unsigned long offset_low,
+		unsigned long *__p)
+{
+    return sys_llseek (fd, offset_high, offset_low, (loff_t *)__p[0], (unsigned int)__p[1]);
+}
+
+asmlinkage int
+sys_arm_mount (char *devname, char *dirname, char *type, unsigned long *__p)
+{
+    return sys_mount (devname, dirname, type, __p[0], (void *)__p[1]);
+}
diff -urNwbB linux/arch/arm/kernel/time.c linux.arm/arch/arm/kernel/time.c
--- linux/arch/arm/kernel/time.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/time.c	Sun Mar  3 13:07:50 1996
@@ -0,0 +1,173 @@
+/*
+ *  linux/arch/arm/kernel/time.c
+ *
+ *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
+ *  Modifications for ARM processor (C) 1994, 1995, 1996 Russell King
+ *
+ * This file contains the ARM-specific time handling details:
+ * reading the RTC at bootup, etc...
+ *
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#include <linux/timex.h>
+#include <asm/irq-no.h>
+
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+#endif
+
+unsigned long do_dummy_gettimeoffset(void)
+{
+	return 0;
+}
+
+static unsigned long (*do_gettimeoffset)(void) = do_dummy_gettimeoffset;
+
+void do_gettimeofday(struct timeval *tv)
+{
+	unsigned long flags;
+
+	save_flags (flags);
+	cli();
+	*tv = xtime;
+	tv->tv_usec += do_gettimeoffset();
+	if (tv->tv_usec >= 1000000) {
+		tv->tv_usec -= 1000000;
+		tv->tv_sec++;
+	}
+	restore_flags(flags);
+}
+
+void do_settimeofday(struct timeval *tv)
+{
+	cli();
+	/* This is revolting. We need to set the xtime.tv_usec
+	 * correctly. However, the value in this location is
+	 * is value at the last tick.
+	 * Discover what correction gettimeofday
+	 * would have done, and then undo it!
+	 */
+	tv->tv_usec -= do_gettimeoffset();
+
+	if (tv->tv_usec < 0) {
+		tv->tv_usec += 1000000;
+		tv->tv_sec--;
+	}
+
+	xtime = *tv;
+	time_state = TIME_BAD;
+	time_maxerror = 0x70000000;
+	time_esterror = 0x70000000;
+	sti();
+}
+
+/*
+ * In order to set the CMOS clock precisely, set_rtc_mmss has to be
+ * called 500 ms after the second nowtime has started, because when
+ * nowtime is written into the registers of the CMOS clock, it will
+ * jump to the next second precisely 500 ms later. Check the Motorola
+ * MC146818A or Dallas DS12887 data sheet for details.
+ */
+int set_rtc_mmss(unsigned long nowtime)
+{
+  int retval = 0;
+  return retval;
+}
+
+/* last time the cmos clock got updated */
+static long last_rtc_update = 0;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick.
+ */
+static inline void timer_interrupt(int irq, struct pt_regs *regs)
+{
+	do_timer(regs);
+
+	/* If we have an externally synchronized linux clock, then update
+	 * CMOS clock accordingly every ~11 minutes.  Set_rtc_mmss() has to be
+	 * called as close as possible to 500 ms before the new second starts.
+	 */
+	if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
+	    xtime.tv_usec > 50000 - (tick >> 1) &&
+	    xtime.tv_usec < 50000 + (tick >> 1))
+	  if (set_rtc_mmss(xtime.tv_sec) == 0)
+	    last_rtc_update = xtime.tv_sec;
+	  else
+	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static inline unsigned long mktime(unsigned int year, unsigned int mon,
+	unsigned int day, unsigned int hour,
+	unsigned int min, unsigned int sec)
+{
+	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
+		mon += 12;	/* Puts Feb last since it has leap day */
+		year -= 1;
+	}
+	return (((
+	    (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+	      year*365 - 719499
+	    )*24 + hour /* now have hours */
+	   )*60 + min /* now have minutes */
+	  )*60 + sec; /* finally seconds */
+}
+
+void time_init(void)
+{
+	unsigned int year, mon, day, hour, min, sec;
+	char buf[8];
+	extern int iic_control(unsigned char, int, char *, int);
+
+	iic_control(0xa0, 2, buf, 5);
+	sec = buf[0];
+	min = buf[1];
+	hour= buf[2];
+	day = buf[3] & 0x3f;
+	mon = buf[4] & 0x1f;
+	iic_control(0xa0, 0xc0, buf, 1);
+	year= buf[0];
+	BCD_TO_BIN(sec);
+	BCD_TO_BIN(min);
+	BCD_TO_BIN(hour);
+	BCD_TO_BIN(day);
+	BCD_TO_BIN(mon);
+
+	if ((year += 1900) < 1970)
+		year += 100;
+	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+	xtime.tv_usec = 0;
+
+	if (request_irq(TIMER_IRQ, timer_interrupt, 0, "timer") != 0)
+		panic("Could not allocate timer IRQ!");
+}
+
diff -urNwbB linux/arch/arm/kernel/traps.c linux.arm/arch/arm/kernel/traps.c
--- linux/arch/arm/kernel/traps.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/traps.c	Sat Feb 24 09:36:57 1996
@@ -0,0 +1,197 @@
+/*
+ *  linux/arch/arm/kernel/traps.c
+ *
+ *  Copyright (C) 1995, 1996 Russell King
+ *  Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
+ */
+
+/*
+ * 'traps.c' handles hardware exceptions after we have saved some state in
+ * 'linux/arch/arm/lib/traps.S'.  Mostly a debugging aid, but will probably
+ * kill the offending process.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret);
+
+static char *proc_mode[]= { "USER", "FIQ", "IRQ", "SVC" };
+static char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
+
+int kstack_depth_to_print = 200;
+
+static inline void console_verbose(void)
+{
+    extern int console_loglevel;
+    console_loglevel = 15;
+}
+
+void register_dump(struct pt_regs *regs)
+{
+    unsigned long pc, flags;
+
+    pc = regs->ARM_pc & ~0xFC000003;
+    flags = regs->ARM_pc & 0xFC000003;
+
+    printk("pc : %08lX  lr : %08lX  sp : %08lX  ip : %08lX\n",
+			pc, regs->ARM_lr, regs->ARM_sp, regs->ARM_ip);
+    printk("fp : %08lX  r10: %08lX  r9 : %08lX  r8 : %08lX\n",
+			regs->ARM_fp, regs->ARM_r10, regs->ARM_r9, regs->ARM_r8);
+    printk("r7 : %08lX  r6 : %08lX  r5 : %08lX  r4 : %08lX\n",
+			regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4);
+    printk("r3 : %08lX  r2 : %08lX  r1 : %08lX  r0 : %08lX\n",
+			regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0);
+    printk("Flags: %c%c%c%c",flags & 0x80000000?'N':'n',
+			flags & 0x40000000?'Z':'z',
+			flags & 0x20000000?'C':'c',
+			flags & 0x10000000?'V':'v');
+
+    printk("  IRQs %s  FIQs %s  Mode %s\n",flags & 0x08000000 ? "off" : "on",
+			flags & 0x04000000 ? "off" : "on", proc_mode[flags & 3]);
+}
+
+static int verify_stack_pointer (int mode, int pid, unsigned long stackptr, int size)
+{
+    if (pid == 0) {
+	if (stackptr < 0x01800000 || stackptr > 0x02000000)
+	    return -EFAULT;
+    } else
+    if (mode == 0) {
+	if (stackptr < 0x01800000)
+	    return verify_area (VERIFY_READ, (void *)stackptr, size);
+	    return -EFAULT;
+    } else
+    if (stackptr < 0x02048000 || stackptr > 0x03000000)
+        return -EFAULT;
+    return 0;
+}
+
+static void dump_stack (unsigned long *start, unsigned long *end, int offset, int max)
+{
+    unsigned long *p;
+    int i;
+
+    for (p = start + offset, i = 0; i < max && p < end; i++, p++) {
+	if ((i & 7) == 0 && i)
+	    printk ("\n       ");
+	printk ("%08lx ", *p);
+    }
+    printk ("\n");
+}
+
+void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
+{
+    static int died=0;
+    int i, mode, pc;
+    unsigned long cstack, sstack, frameptr;
+	
+    if(died)
+	while(1);
+    died = 1;
+
+    mode = regs->ARM_pc & 3;
+    pc = regs->ARM_pc & ~0xfc000003;
+
+    switch (mode) {
+	case 3:  cstack = (unsigned long)(regs + 1);     break;
+	case 0:  cstack = (unsigned long)regs->ARM_sp;   break;
+	default: cstack = 0;
+    }
+    sstack = (unsigned long)current->kernel_stack_page;
+
+    console_verbose();
+
+    printk("===============================================================\n");
+    printk("Internal error: %s", str);
+    if (err)
+	printk(": %04x", err & 0xffff);
+    printk("\nCPU: %d\n", smp_processor_id());
+	
+    register_dump(regs);
+
+    printk("Process %s (pid: %d, stackpage=%08lx)\n",
+	current->comm, current->pid, current->kernel_stack_page);
+
+    printk("Stack: ");
+    if (*(unsigned long *)sstack != STACK_MAGIC)
+	printk("*** corrupted stack page\n       ");
+
+    if (mode != 0) {
+	if (verify_stack_pointer (mode, current->pid, cstack, 4))
+	    printk ("%08lx invalid kernel stack\n", cstack);
+	else
+	if(cstack > sstack + 4096)
+	    printk("(sp overflow)\n");
+	else
+	if(cstack < sstack)
+	    printk("(sp underflow)\n");
+	else
+	    dump_stack ((unsigned long *)sstack, (unsigned long *)sstack + 1024,
+		cstack - sstack, kstack_depth_to_print);
+    } else {
+	if (verify_stack_pointer (mode, current->pid, cstack, 0x1800000 - cstack))
+	    printk ("%08lx invalid user stack\n", cstack);
+	else
+	    dump_stack ((unsigned long *)cstack, (unsigned long *)0x01800000, 0,
+	    	kstack_depth_to_print);
+    }
+
+    printk("Backtrace: \n");
+    frameptr = regs->ARM_fp;
+
+    if (verify_stack_pointer (mode, current->pid, frameptr, 4))
+	printk("invalid frame pointer\n");
+    else {
+	extern void c_backtrace (unsigned long);
+	c_backtrace (frameptr);
+    }
+
+    printk("Code: ");
+    for (i = -2; i < 3; i++)
+	printk("%08lx ", ((unsigned long *)pc)[i]);
+
+    printk("\n===============================================================\n");
+
+    died = 0;
+
+    if(ret != -1)
+	do_exit(ret);
+    else {
+	cli();
+	while(1);
+    }
+}
+
+asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode)
+{
+    current->tss.error_code = 0;
+    current->tss.trap_no = 6;
+    die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL);
+}
+
+asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode)
+{
+    current->tss.error_code = 0;
+    current->tss.trap_no = 11;
+    die_if_kernel("Oops - address exception", regs, mode, SIGBUS);
+}
+
+asmlinkage void do_unexp_fiq (struct pt_regs *regs)
+{
+#ifndef CONFIG_IGNORE_FIQ
+    printk ("Hmm.  Unexpected FIQ received, but trying to continue\n");
+    printk ("You may have a hardware problem...\n");
+#endif
+}
+
+asmlinkage void bad_mode(struct pt_regs *regs, int reason, int mode)
+{
+    printk("Bad mode in %s handler detected: mode %s",
+		handler[reason],
+		proc_mode[mode]);
+    die_if_kernel("Oops", regs, 0, -1);
+}
diff -urNwbB linux/arch/arm/lib/Makefile linux.arm/arch/arm/lib/Makefile
--- linux/arch/arm/lib/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/Makefile	Sun Feb 11 09:32:40 1996
@@ -0,0 +1,25 @@
+O_TARGET := lib.o
+O_OBJS   := backtrace.o bitops.o delay.o irqs.o \
+	ll_char_wr.o io.o segment.o system.o traps.o ioc.o \
+	calls.o prints.o memfastset.o fp_support.o string.o \
+	floppydma.o
+
+ifdef CONFIG_INET
+O_OBJS += iputils.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+system.o: constants.h
+
+traps.o: constants.h
+
+constants.h: getconstants
+	./getconstants > constants.h
+
+getconstants: getconstants.c
+	gcc -o getconstants getconstants.c -O2 -D__KERNEL__ -fomit-frame-pointer -Wl,-N
+
+irqs.s:	irqs.S
+	gcc -E $^ | tr ";" "\n" > $@
+
diff -urNwbB linux/arch/arm/lib/backtrace.S linux.arm/arch/arm/lib/backtrace.S
--- linux/arch/arm/lib/backtrace.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/backtrace.S	Sun Mar  3 13:09:12 1996
@@ -0,0 +1,40 @@
+/*
+ * linux/arch/arm/lib/backtrace.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+
+		.global	_c_backtrace
+_c_backtrace:	stmfd	sp!, {fp, lr}
+		mov	fp, r0
+		bl	_backtrace
+		LOADREGS(fd, sp!, {fp, pc})
+
+@ fp is 0 or stack frame
+		.global	_backtrace
+_backtrace:	stmfd	sp!, {r4 - r12, lr}
+		teq	fp, #0
+		moveq	r0, #-2
+		LOADREGS(eqfd, sp!, {r4 - r12, pc})
+loop:		tst	fp, #0xFC000003
+		bne	loop
+		ldr	r1, [fp, #0]
+		sub	r1, r1, #12
+		ldr	r2, [fp, #-4]
+		ldr	r3, [fp, #-8]
+		mov	r0, fp
+		ldr	fp, [fp, #-12]
+		teq	r0, fp
+		moveq	fp, #0
+		adr	r0, message
+ 		bl	_printk
+		teq	fp, #0
+		bne	loop
+		LOADREGS(fd, sp!, {r4 - r12, pc})
+
+message:	.ascii	"Func: entered at %p, link %p, sp %p\n"
+		.byte 0
+		.align
diff -urNwbB linux/arch/arm/lib/bitops.S linux.arm/arch/arm/lib/bitops.S
--- linux/arch/arm/lib/bitops.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/bitops.S	Sun Mar  3 13:09:39 1996
@@ -0,0 +1,129 @@
+/*
+ * linux/arch/arm/lib/bitops.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+
+@ Purpose  : Function to set a bit
+@ Prototype: int set_bit(int bit,int *addr)
+
+		.global	_set_bit
+_set_bit:	add	r1, r1, r0, lsr #3	@ Get byte offset
+		and	r3, r0, #7		@ Get bit offset
+		mov	r0, #1
+		SAVEIRQS
+		DISABLEIRQS
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		orr	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS
+		RETINSTR(mov,pc,lr)
+
+@ Purpose  : Function to clear a bit
+@ Prototype: int clear_bit(int bit,int *addr)
+
+		.global	_clear_bit
+_clear_bit:	add	r1, r1, r0, lsr #3	@ Get byte offset
+		and	r3, r0, #7	@ Get bit offset
+		mov	r0, #1
+		SAVEIRQS
+		DISABLEIRQS
+		ldrb	r2, [r1]
+		tst	r2, r0, lsl r3
+		bic	r2, r2, r0, lsl r3
+		moveq	r0, #0
+		strb	r2, [r1]
+		RESTOREIRQS
+		RETINSTR(mov,pc,lr)
+
+@ Purpose  : Function to test a bit
+@ Prototype: int test_bit(int bit,int *addr)
+
+		.global	_test_bit
+_test_bit:	ldrb	r2, [r1, r0, lsr#3]
+		and	r1, r0, #7
+		mov	r0, #1
+		and	r0, r2, r0, lsl r1
+		RETINSTR(mov,pc,lr)
+
+@ Purpose  : Function to change a bit
+@ Prototype: int change_bit(int bit,int *addr)
+
+		.global	_change_bit
+_change_bit:	SAVEIRQS
+		DISABLEIRQS
+		add	r1, r1, r0,LSR#3
+		ldrb	r2, [r1]
+		and	r3, r0, #7
+		mov	r0, #1
+		mov	r0, r0, lsl r3
+		tst	r2, r0
+		eor	r2, r2, r0
+		strb	r2, [r1]
+		moveq	r0, #0
+		RESTOREIRQS
+		RETINSTR(mov,pc,lr)
+
+@ Purpose  : Find a 'zero' bit
+@ Prototype: int find_first_zero_bit(char *addr,int maxbit);
+
+		.global	_find_first_zero_bit
+_find_first_zero_bit:
+		mov	r2, #0			@ Initialise bit position
+findzbit1lp:	ldrb	r3, [r0, r2, lsr #3]	@ Check byte, if 0xFF, then all bits set
+		teq	r3, #0xFF
+		bne	foundzbit
+		add	r2, r2, #8
+		cmp	r2, r1			@ Check to see if we have come to the end
+		bcc	findzbit1lp
+		add	r0, r1, #1		@ Make sure that we flag an error
+		RETINSTR(mov,pc,lr)
+foundzbit:	tst	r3, #1			@ Check individual bits
+		moveq	r0, r2
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #2
+		addeq	r0, r2, #1
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #4
+		addeq	r0, r2, #2
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #8
+		addeq	r0, r2, #3
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #16
+		addeq	r0, r2, #4
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #32
+		addeq	r0, r2, #5
+		RETINSTR(moveq,pc,lr)
+		tst	r3, #64
+		addeq	r0, r2, #6
+		RETINSTR(moveq,pc,lr)
+		add	r0, r2, #7
+		RETINSTR(mov,pc,lr)
+
+@ Purpose  : Find next 'zero' bit
+@ Prototype: int find_next_zero_bit(char *addr,int maxbit,int offset)
+
+		.global	_find_next_zero_bit
+_find_next_zero_bit:
+		add	r2, r2, #1		@ Increment bit position
+		tst	r2, #7
+		beq	findzbit1lp		@ If new byte, goto old routine
+		ldrb	r3, [r0, r2, lsr#3]
+		orr	r3, r3, #0xFF00		@ Set top bits so we wont get confused
+		stmfd	sp!, {r4}
+		and	r4, r2, #7
+		mov	r3, r3, lsr r4		@ Shift right by no. of bits
+		ldmfd	sp!, {r4}
+		and	r3, r3, #0xFF
+		teq	r3, #0xFF
+		orreq	r2, r2, #7
+		addeq	r2, r2, #1
+		beq	findzbit1lp		@ If all bits are set, goto old routine
+		b	foundzbit
diff -urNwbB linux/arch/arm/lib/calls.c linux.arm/arch/arm/lib/calls.c
--- linux/arch/arm/lib/calls.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/calls.c	Sun Mar  3 13:10:14 1996
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/lib/calls.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ *
+ * Create a table for system calls
+ */
+
+#include <linux/sys.h>
+
+extern void sys_ni_syscall(void);
+
+#define sysname(name) extern void name(void);
+#include "calls.h"
+#undef sysname
+
+#define sysname(name) name ,
+
+void (*sys_call_table[])(void)=
+{
+#include "calls.h"
+  sys_ni_syscall
+};
+
+#undef sysname
diff -urNwbB linux/arch/arm/lib/calls.h linux.arm/arch/arm/lib/calls.h
--- linux/arch/arm/lib/calls.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/calls.h	Sun Mar  3 13:10:41 1996
@@ -0,0 +1,150 @@
+/*
+ * linux/arch/arm/lib/calls.h
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+/* 0 */		sysname(sys_setup)
+		sysname(sys_exit)
+		sysname(sys_fork)
+		sysname(sys_read)
+		sysname(sys_write)
+/* 5 */		sysname(sys_open)
+		sysname(sys_close)
+		sysname(sys_waitpid)
+		sysname(sys_creat)
+		sysname(sys_link)
+/* 10 */	sysname(sys_unlink)
+		sysname(sys_execve)
+		sysname(sys_chdir)
+		sysname(sys_time)
+		sysname(sys_mknod)
+/* 15 */	sysname(sys_chmod)
+		sysname(sys_chown)
+		sysname(sys_break)
+		sysname(sys_stat)
+		sysname(sys_lseek)
+/* 20 */	sysname(sys_getpid)
+		sysname(sys_arm_mount)		/* *** 5 *** */
+		sysname(sys_umount)
+		sysname(sys_setuid)
+		sysname(sys_getuid)
+/* 25 */	sysname(sys_stime)
+		sysname(sys_ptrace)
+		sysname(sys_alarm)
+		sysname(sys_fstat)
+		sysname(sys_pause)
+/* 30 */	sysname(sys_utime)
+		sysname(sys_stty)
+		sysname(sys_gtty)
+		sysname(sys_access)
+		sysname(sys_nice)
+/* 35 */	sysname(sys_ftime)
+		sysname(sys_sync)
+		sysname(sys_kill)
+		sysname(sys_rename)
+		sysname(sys_mkdir)
+/* 40 */	sysname(sys_rmdir)
+		sysname(sys_dup)
+		sysname(sys_pipe)
+		sysname(sys_times)
+		sysname(sys_prof)
+/* 45 */	sysname(sys_brk)
+		sysname(sys_setgid)
+		sysname(sys_getgid)
+		sysname(sys_signal)
+		sysname(sys_geteuid)
+/* 50 */	sysname(sys_getegid)
+		sysname(sys_acct)
+		sysname(sys_phys)
+		sysname(sys_lock)
+		sysname(sys_ioctl)
+/* 55 */	sysname(sys_fcntl)
+		sysname(sys_mpx)
+		sysname(sys_setpgid)
+		sysname(sys_ulimit)
+		sysname(sys_olduname)
+/* 60 */	sysname(sys_umask)
+		sysname(sys_chroot)
+		sysname(sys_ustat)
+		sysname(sys_dup2)
+		sysname(sys_getppid)
+/* 65 */	sysname(sys_getpgrp)
+		sysname(sys_setsid)
+		sysname(sys_sigaction)
+		sysname(sys_sgetmask)
+		sysname(sys_ssetmask)
+/* 70 */	sysname(sys_setreuid)
+		sysname(sys_setregid)
+		sysname(sys_sigsuspend)
+		sysname(sys_sigpending)
+		sysname(sys_sethostname)
+/* 75 */	sysname(sys_setrlimit)
+		sysname(sys_getrlimit)
+		sysname(sys_getrusage)
+		sysname(sys_gettimeofday)
+		sysname(sys_settimeofday)
+/* 80 */	sysname(sys_getgroups)
+		sysname(sys_setgroups)
+		sysname(old_select)
+		sysname(sys_symlink)
+		sysname(sys_lstat)
+/* 85 */	sysname(sys_readlink)
+		sysname(sys_uselib)
+		sysname(sys_swapon)
+		sysname(sys_reboot)
+		sysname(old_readdir)
+/* 90 */	sysname(old_mmap)
+		sysname(sys_munmap)
+		sysname(sys_truncate)
+		sysname(sys_ftruncate)
+		sysname(sys_fchmod)
+/* 95 */	sysname(sys_fchown)
+		sysname(sys_getpriority)
+		sysname(sys_setpriority)
+		sysname(sys_profil)
+		sysname(sys_statfs)
+/* 100 */	sysname(sys_fstatfs)
+		/* sysname(sys_ioperm) */sysname(sys_ni_syscall)/* To do */
+		sysname(sys_socketcall)
+		sysname(sys_syslog)
+		sysname(sys_setitimer)
+/* 105 */	sysname(sys_getitimer)
+		sysname(sys_newstat)
+		sysname(sys_newlstat)
+		sysname(sys_newfstat)
+		sysname(sys_uname)
+/* 110 */	sysname(sys_iopl)
+		sysname(sys_vhangup)
+		sysname(sys_idle)
+		/* sysname(sys_vm86) */sysname(sys_ni_syscall)
+		sysname(sys_wait4)
+/* 115 */	sysname(sys_swapoff)
+		sysname(sys_sysinfo)
+		sysname(sys_ipc)
+		sysname(sys_fsync)
+		sysname(sys_sigreturn)
+		sysname(sys_clone)
+		sysname(sys_setdomainname)
+		sysname(sys_newuname)
+		/* sysname(sys_modify_ldt) */sysname(sys_ni_syscall)/* ??? */
+		sysname(sys_adjtimex)
+/* 125 */	sysname(sys_mprotect)
+		sysname(sys_sigprocmask)
+		sysname(sys_create_module)
+		sysname(sys_arm_init_module)	/* *** 5 *** */
+		sysname(sys_delete_module)
+/* 130 */	sysname(sys_get_kernel_syms)
+		/* sysname(sys_quotactl) */sysname(sys_ni_syscall)
+		sysname(sys_getpgid)
+		sysname(sys_fchdir)
+		sysname(sys_bdflush)
+/* 135 */	sysname(sys_sysfs)
+		sysname(sys_personality)
+		/* sysname(sys_afs_syscall) */sysname(sys_ni_syscall)
+		sysname(sys_setfsuid)
+		sysname(sys_setfsgid)
+/* 140 */	sysname(sys_arm_llseek)		/* *** 5 *** */
+		sysname(sys_getdents)
+		sysname(sys_select)
+		sysname(sys_flock)
+
diff -urNwbB linux/arch/arm/lib/delay.S linux.arm/arch/arm/lib/delay.S
--- linux/arch/arm/lib/delay.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/delay.S	Sun Mar  3 13:11:20 1996
@@ -0,0 +1,43 @@
+/*
+ * linux/arch/arm/lib/delay.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+
+LC0:		.word	_loops_per_sec
+
+		.global	_udelay
+_udelay:	mov	r2, #0x1000
+		orr	r2, r2, #0x00c6
+		mul	r1, r0, r2
+		ldr	r2, [pc, #LC0 - . - 8]
+		ldr	r2, [r2]
+		mov	r1, r1, lsr #16
+		mov	r2, r2, lsr #16
+		mul	r0, r1, r2
+		teq	r0, #0
+		RETINSTR(moveq,pc,lr)
+
+@ Delay routine
+		.global	___delay
+___delay:	subs	r0, r0, #1
+;		RETINSTR(movcc,pc,lr)
+;		subs	r0, r0, #1
+;		RETINSTR(movcc,pc,lr)
+;		subs	r0, r0, #1
+;		RETINSTR(movcc,pc,lr)
+;		subs	r0, r0, #1
+;		RETINSTR(movcc,pc,lr)
+;		subs	r0, r0, #1
+;		RETINSTR(movcc,pc,lr)
+;		subs	r0, r0, #1
+;		RETINSTR(movcc,pc,lr)
+;		subs	r0, r0, #1
+;		RETINSTR(movcc,pc,lr)
+;		subs	r0, r0, #1
+		bcs	___delay
+		RETINSTR(mov,pc,lr)
+
diff -urNwbB linux/arch/arm/lib/floppydma.S linux.arm/arch/arm/lib/floppydma.S
--- linux/arch/arm/lib/floppydma.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/floppydma.S	Sun Mar  3 13:11:49 1996
@@ -0,0 +1,73 @@
+/*
+ * linux/arch/arm/lib/floppydma.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+
+LC0:		.word	_floppyfiq_len
+
+		.global	_floppy_fiqin_start
+		.global	_floppy_fiqin_end
+_floppy_fiqin_start:
+		subs	r9, r9, #1
+		ldrgtb	r12, [r11, #-4]
+		ldrleb	r12, [r11], #0
+		strb	r12, [r10], #1
+		subs	pc, lr, #4
+_floppy_fiqin_end:
+
+		.global	_floppy_fiqout_start
+		.global	_floppy_fiqout_end
+_floppy_fiqout_start:
+		subs	r9, r9, #1
+		ldrgeb	r12, [r10], #1
+		movlt	r12, #0
+		strleb	r12, [r11], #0
+		subles	pc, lr, #4
+		strb	r12, [r11, #-4]
+		subs	pc, lr, #4
+_floppy_fiqout_end:
+
+@ Params:
+@ r0 = length
+@ r1 = address
+@ r2 = floppy port
+		.global	_floppy_fiqsetup
+_floppy_fiqsetup:
+		mov	ip, sp
+		stmfd	sp!, {fp, ip, lr, pc}
+		sub	fp, ip, #4
+		mov	r3, pc
+		teqp	pc, #0x0c000001		@ disable FIQs, IRQs, FIQ mode
+		mov	r0, r0			@ NOP
+		mov	r9, r0
+		mov	r10, r1
+		mov	r11, r2
+		teqp	r3, #0			@ back to normal
+		mov	r0, r0			@ NOP
+		ldmea	fp, {fp, sp, pc}^
+
+		.global	_floppy_fiqresidual
+_floppy_fiqresidual:
+		mov	ip, sp
+		stmfd	sp!, {fp, ip, lr, pc}
+		sub	fp, ip, #4
+		mov	r3, pc
+		teqp	pc, #0x0c000001		@ disable FIQs, IRQs, FIQ mode
+		mov	r0, r0			@ NOP
+		mov	r0, r9
+		teqp	r3, #0
+		mov	r0, r0			@ NOP
+		ldmea	fp, {fp, sp, pc}^
+		.data
+
+		.global	_floppyfiq_len
+		.global	_floppyfiq_to
+		.global	_floppyfiq_from
+
+_floppyfiq_len:		.word	0
+_floppyfiq_to:		.word	0
+_floppyfiq_from:	.word	0
diff -urNwbB linux/arch/arm/lib/fp_support.c linux.arm/arch/arm/lib/fp_support.c
--- linux/arch/arm/lib/fp_support.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/fp_support.c	Sun Mar  3 13:12:25 1996
@@ -0,0 +1,29 @@
+/*
+ * linux/arch/arm/lib/fp_support.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/sched.h>
+
+extern void (*fp_save)(void *);
+
+__asm__(
+	".stabs \"_fp_printk\", 11, 0, 0, 0\n\t"
+	".stabs \"_printk\", 1, 0, 0, 0\n\t"
+	".stabs \"_fp_current\", 11, 0, 0, 0\n\t"
+	".stabs \"_current_set\", 1, 0, 0, 0\n\t"
+	".stabs \"_fp_send_sig\", 11, 0, 0, 0\n\t"
+	".stabs \"_send_sig\", 1, 0, 0, 0\n\t");
+
+void fp_setup(void)
+{
+	struct task_struct **p;
+
+	p = task;
+	while (p++ < task + NR_TASKS)
+	{
+		if(*p)
+			fp_save((*p)->tss.fpe_save);
+	}
+}
diff -urNwbB linux/arch/arm/lib/getconstants.c linux.arm/arch/arm/lib/getconstants.c
--- linux/arch/arm/lib/getconstants.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/getconstants.c	Sun Mar  3 13:12:50 1996
@@ -0,0 +1,51 @@
+/*
+ * linux/arch/arm/lib/getconstants.c
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <linux/mm.h>
+#include <asm/pgtable.h>
+#include <stdio.h>
+#include <linux/unistd.h>
+
+void printdef(char *def, int no)
+{
+	printf("#define %s\t%d\n", def, no);
+}
+
+#define STRING(x) #x
+
+int old = 0;
+
+int main()
+{
+	struct task_struct *tsk = NULL;
+	struct mm_struct *mm = NULL;
+	printf("/*\n *  contants.h generated by getconstants\n * DO NOT EDIT!\n */\n");
+
+	if (!old) printf("#define _current\t_%s\n", "current_set");
+
+	printdef("PAGE_PRESENT",	_PAGE_PRESENT);
+	printdef("PAGE_RW",		_PAGE_RW);
+	printdef("PAGE_USER",		_PAGE_USER);
+	printdef("PAGE_ACCESSED",	_PAGE_ACCESSED);
+	printdef("PAGE_DIRTY",		_PAGE_DIRTY);
+	if (old)
+	printdef("TSS_MEMMAP",		(int)&tsk->tss.memmap);
+	else
+	{
+	printdef("TSK_MM",		(int)&tsk->mm);
+	printdef("MM_PGDIR",		(int)&mm->pgd);
+	}
+	printdef("TSS_UREGS",		(int)&tsk->tss.uregs);
+	printdef("TSS_SREGS",		(int)&tsk->tss.sregs);
+	printdef("TSS_FPESAVE",		(int)&tsk->tss.fpe_save);
+
+	printdef("SYS_execve",		__NR_execve);
+	printdef("SYS_sigreturn",	__NR_sigreturn);
+	printf("#define KSWI_BASE	0x900000\n");
+	printf("#define KSWI_SYS_BASE	0x9F0000\n");
+	printf("#define SYS_ERROR0	0x9F0000\n");
+	return 0;
+}
diff -urNwbB linux/arch/arm/lib/io.S linux.arm/arch/arm/lib/io.S
--- linux/arch/arm/lib/io.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/io.S	Sun Mar  3 13:13:17 1996
@@ -0,0 +1,277 @@
+/*
+ * linux/arch/arm/lib/io.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+
+#define OUT(reg)					\
+		mov	r8, reg, lsl \#16		;\
+		orr	r8, r8, r8, lsr\#16		;\
+		str	r8, [r3, r0, lsl\#2]		;\
+		mov	r8, reg, lsr\#16		;\
+		orr	r8, r8, r8, lsl\#16		;\
+		str	r8, [r3, r0, lsl\#2]
+
+#define IN(reg)						\
+		ldr	reg, [r0]			;\
+		and	reg, reg, ip			;\
+		ldr	lr, [r0]			;\
+		orr	reg, reg, lr, lsl\#16
+
+@ purpose: read a byte from a hardware register.
+@ Proto  : unsigned char inb(int address);
+
+		.align
+
+		.global	___inb
+		.global	___inb_p
+		.global	___inbc
+		.global	___inbc_p
+___inb:
+___inb_p:
+___inbc:
+___inbc_p:
+		cmp	r0, #0x00c00000
+		movge	r2, #0
+		movlt	r2, #0x03000000
+		orrlt	r2, r2, #0x00010000
+		ldrb	r0, [r2, r0, lsl#2]
+		RETINSTR(mov,pc,lr)
+
+@ purpose: read a short word from a hardware register.
+@ Proto  : unsigned char inw(int address);
+
+		.align
+
+		.global	___inw
+___inw:		cmp	r0, #0x00c00000
+		movge	r2, #0
+		movlt	r2, #0x03000000
+		orrlt	r2, r2, #0x00010000
+		ldr	r0, [r2, r0, lsl#2]
+		bic	r0, r0, #0xff000000
+		bic	r0, r0, #0x00ff0000
+		RETINSTR(mov,pc,lr)
+
+@ Purpose: write a byte to a hardware register.
+@ Proto  : outb(unsigned char c,int address);
+
+		.align
+
+		.global	___outb
+		.global	___outb_p
+		.global	___outbc
+		.global	___outbc_p
+___outb:
+___outb_p:
+___outbc:
+___outbc_p:
+		cmp	r1,#0x00c00000
+		movge	r2,#0
+		movlt	r2, #0x03000000
+		orrlt	r2, r2, #0x00010000
+		strb	r0,[r2,r1,LSL#2]
+		RETINSTR(mov,pc,lr)
+
+@ Purpose: write a short to a hardware register.
+@ Proto  : outw(unsigned short c,int address);
+
+		.align
+
+		.global	___outw
+___outw:	cmp	r1, #0x00c00000
+		movge	r2, #0
+		movlt	r2, #0x03000000
+		orrlt	r2, r2, #0x00010000
+		orr	r0, r0, r0, lsl#16
+		str	r0, [r2, r1, lsl#2]
+		RETINSTR(mov,pc,lr)
+
+@ Purpose: read a block of data from a hardware register to memory.
+@ Proto  : insw(int from_port, void *to, int len_in_words);
+@ Proto  : inswb(int from_port, void *to, int len_in_bytes);
+@ Notes  : increment to
+
+		.align
+
+		.global	_insw
+		.global	_inswb
+_insw:		mov	r2, r2, lsl#1
+_inswb:		mov	ip, sp
+		stmfd	sp!, {r4 - r10 ,fp ,ip ,lr ,pc}
+		sub	fp, ip, #4
+		cmp	r0, #0x00C00000
+		movge	r3, #0
+		movlt	r3, #0x03000000
+		orrlt	r3, r3, #0x00010000
+		add	r0, r3, r0, lsl #2
+		tst	r1, #3
+		beq	pcinswok
+		tst	r1, #1
+		bne	pcinsw_notaligned
+		cmp	r2, #1
+		ldrge	r4, [r0]
+		strgeb	r4, [r1], #1
+		movgt	r4, r4, LSR#8
+		strgtb	r4, [r1], #1
+		ldmleea	fp, {r4 - r10, fp, sp, pc}^
+		sub	r2, r2, #2
+pcinswok:	mov	ip, #0xFF
+		orr	ip, ip, ip, lsl #8
+pcinswlp:	subs	r2, r2, #64
+		bmi	pcinsw_toosmall
+		IN(r3)
+		IN(r4)
+		IN(r5)
+		IN(r6)
+		IN(r7)
+		IN(r8)
+		IN(r9)
+		IN(r10)
+		stmia	r1!, {r3 - r10}
+		IN(r3)
+		IN(r4)
+		IN(r5)
+		IN(r6)
+		IN(r7)
+		IN(r8)
+		IN(r9)
+		IN(r10)
+		stmia	r1!, {r3 - r10}
+		bne	pcinswlp
+		LOADREGS(ea, fp, {r4 - r10, fp, sp, pc})
+pcinsw_toosmall:
+		add	r2, r2, #32
+		bmi	pcinsw_toosmall2
+pcinsw2lp:	IN(r3)
+		IN(r4)
+		IN(r5)
+		IN(r6)
+		IN(r7)
+		IN(r8)
+		IN(r9)
+		IN(r10)
+		stmia	r1!, {r3 - r10}
+		LOADREGS(eqea, fp, {r4 - r10, fp, sp, pc})
+		b	pcinsw_notaligned
+pcinsw_toosmall2:
+		add	r2, r2, #32
+pcinsw_notaligned:
+		cmp	r2, #1
+		LOADREGS(ltea, fp, {r4 - r10, fp, sp, pc})
+		ldr	r4, [r0]
+		strb	r4, [r1], #1
+		movgt	r4, r4, LSR#8
+		strgtb	r4, [r1], #1
+		subs	r2, r2, #2
+		bgt	pcinsw_notaligned
+		LOADREGS(ea, fp, {r4 - r10, fp, sp, pc})
+
+@ Purpose: write a block of data from memory to a hardware register.
+@ Proto  : outsw(int to_reg, void *from, int len_in_words);
+@ Proto  : outswb(int to_reg, void *from, int len_in_bytes);
+@ Notes  : increments from
+
+		.align
+
+		.global	_outsw
+		.global	_outswb
+_outsw:		mov	r2, r2, LSL#1
+_outswb:	mov	ip, sp
+		stmfd	sp!, {r4 - r8, fp, ip, lr, pc}
+		sub	fp, ip, #4
+		cmp	r0, #0x00C00000
+		movge	r3, #0
+		movlt	r3, #0x03000000
+		orrlt	r3, r3, #0x00010000
+		tst	r1, #2
+		beq	pcoutsw32lp
+		ldr	r4, [r1], #2
+		mov	r4, r4, lsl #16
+		orr	r4, r4, r4, lsr #16
+		str	r4, [r3, r0, lsl #2]
+		sub	r2, r2, #2
+		teq	r2, #0
+		LOADREGS(eqea, fp, {r4 - r8, fp, sp, pc})
+pcoutsw32lp:	subs	r2,r2,#32
+		blt	pcoutsw_toosmall
+		ldmia	r1!,{r4,r5,r6,r7}
+		OUT(r4)
+		OUT(r5)
+		OUT(r6)
+		OUT(r7)
+		ldmia	r1!,{r4,r5,r6,r7}
+		OUT(r4)
+		OUT(r5)
+		OUT(r6)
+		OUT(r7)
+		LOADREGS(eqea, fp, {r4 - r8, fp, sp, pc})
+		b	pcoutsw32lp
+pcoutsw_toosmall:
+		adds	r2,r2,#32
+		LOADREGS(eqea, fp, {r4 - r8, fp, sp, pc})
+lpx:		ldr	r4,[r1],#2
+		mov	r4,r4,LSL#16
+		orr	r4,r4,r4,LSR#16
+		str	r4,[r3,r0,LSL#2]
+		subs	r2,r2,#2
+		bgt	lpx
+		LOADREGS(ea, fp, {r4 - r8, fp, sp, pc})
+
+@ Purpose: write a vidc register.
+@ Proto  : void vidc_write(int register, int value);
+@ Returns: nothing
+
+		.align
+
+		.global	_vidc_write
+_vidc_write:	bic	r1, r1, #0xFF000000
+		orr	r0, r1, r0, lsl #24
+		mov	r1, #0x03400000
+		str	r0, [r1]
+		RETINSTR(mov,pc,lr)
+
+@ Purpose: write a memc register
+@ Proto  : void memc_write(int register, int value);
+@ Returns: nothing
+
+		.align
+
+		.global	_memc_write
+_memc_write:	cmp	r0, #7
+		RETINSTR(movgt,pc,lr)
+		mov	r0, r0, lsl #17
+		mov	r1, r1, lsl #15
+		mov	r1, r1, lsr #17
+		orr	r0, r0, r1, lsl #2
+		add	r0, r0, #0x03600000
+		strb	r0, [r0]
+		RETINSTR(mov,pc,lr)
+
+@ Purpose: call an expansion card loader to read bytes.
+@ Proto  : int read_loader(int offset, int card_base, int *loader);
+@ Returns: byte read
+
+		.align
+
+		.global	_read_loader
+_read_loader:	stmfd	sp!, {r4 - r12, lr}
+		mov	r11, r1
+		mov	r1, r0
+		mov	lr, pc
+		mov	pc, r2
+		LOADREGS(fd, sp!, {r4 - r12, pc})
+
+		.align
+msgg:
+		.ascii	"%08X  "
+		.align
+
+show:		stmfd	sp!, {r0-r12, lr}
+		mov	r1, r8
+		adr	r0, msgg
+		bl	_printk
+		LOADREGS(fd, sp!, {r0-r12, pc})
diff -urNwbB linux/arch/arm/lib/ioc.c linux.arm/arch/arm/lib/ioc.c
--- linux/arch/arm/lib/ioc.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/ioc.c	Sun Feb 11 09:32:40 1996
@@ -0,0 +1,7 @@
+#include <asm/io.h>
+
+unsigned long muldiv(unsigned long lps, unsigned long hz, int ticks)
+{
+	return lps * hz / ticks;
+}
+
diff -urNwbB linux/arch/arm/lib/iputils.S linux.arm/arch/arm/lib/iputils.S
--- linux/arch/arm/lib/iputils.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/iputils.S	Sun Mar  3 13:14:06 1996
@@ -0,0 +1,385 @@
+/*
+ * linux/arch/arm/lib/iputils.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+@ r0 = buffer
+@ r1 = len
+@ r2 = sum
+		.global	_csum_partial
+_csum_partial:	tst	r0, #2
+		beq	alignok
+		subs	r1, r1, #2
+		addmi	r1, r1, #2
+		bmi	nobits32
+		bic	r0, r0, #3
+		ldr	r3, [r0], #4
+		adds	r2, r2, r3, lsr #16
+		adcs	r2, r2, #0
+alignok:	adds	r2, r2, #0
+		bics	ip, r1, #31
+		beq	nolots32
+		stmfd	sp!, {r4 - r10}
+lp32:		ldmia	r0!, {r3 - r10}
+		adcs	r2, r2, r3
+		adcs	r2, r2, r4
+		adcs	r2, r2, r5
+		adcs	r2, r2, r6
+		adcs	r2, r2, r7
+		adcs	r2, r2, r8
+		adcs	r2, r2, r9
+		adcs	r2, r2, r10
+		sub	ip, ip, #32
+		teq	ip, #0
+		bne	lp32
+		adcs	r2, r2, #0
+		ldmfd	sp!, {r4 - r10}
+nolots32:	ands	ip, r1, #0x1c
+		beq	nobits32
+bits32lp:	ldr	r3, [r0], #4
+		adcs	r2, r2, r3
+		sub	ip, ip, #4
+		teq	ip, #0
+		bne	bits32lp
+		adcs	r2, r2, #0
+nobits32:	ands	ip, r1, #3
+		moveq	r0, r2
+		moveqs	pc, lr
+		mov	ip, ip, lsl #3
+		rsb	ip, ip, #32
+		ldr	r3, [r0]
+		mov	r3, r3, lsl ip
+		adds	r2, r2, r3, lsr ip
+		adc	r0, r2, #0
+		movs	pc, lr
+
+@ r0 = src
+@ r1 = dst
+@ r2 = len
+@ r3 = sum
+		.global	___csum_partial_copy_fromuser
+___csum_partial_copy_fromuser:
+		mov	ip, sp
+		stmfd	sp!, {r4 - r10, fp, ip, lr, pc}
+		sub	fp, ip, #4
+		tst	r1, #2			@ Test destination alignment
+		beq	dst_aligned
+		subs	r2, r2, #2
+		addmi	r2, r2, #2
+		bmi	nobits32_b
+		ldrt	r4, [r0], #2
+		mov	r5, r4, lsl #16
+		adds	r3, r3, r5, lsr #16
+		adcs	r3, r3, #0
+		strb	r4, [r1], #1
+		mov	r4, r4, lsr #8
+		strb	r4, [r1], #1		@ Destination now aligned
+dst_aligned:	tst	r0, #2
+		bne	src_not_aligned
+		adds	r3, r3, #0
+		bics	ip, r2, #31		@ Routine for src & dst aligned
+		beq	nolots32_ba
+lp32_ba:	ldrt	r4, [r0], #4
+		ldrt	r5, [r0], #4
+		ldrt	r6, [r0], #4
+		ldrt	r7, [r0], #4
+		str	r4, [r1], #4
+		str	r5, [r1], #4
+		str	r6, [r1], #4
+		str	r7, [r1], #4
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		ldrt	r4, [r0], #4
+		ldrt	r5, [r0], #4
+		ldrt	r6, [r0], #4
+		ldrt	r7, [r0], #4
+		str	r4, [r1], #4
+		str	r5, [r1], #4
+		str	r6, [r1], #4
+		str	r7, [r1], #4
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		sub	ip, ip, #32
+		teq	ip, #0
+		bne	lp32_ba
+		adcs	r3, r3, #0
+nolots32_ba:	ands	ip, r2, #0x1c
+		beq	nobits32_ba
+bits32lp_ba:	ldrt	r4, [r0], #4
+		str	r4, [r1], #4
+		adcs	r3, r3, r4
+		sub	ip, ip, #4
+		teq	ip, #0
+		bne	bits32lp_ba
+		adcs	r3, r3, #0
+nobits32_ba:	ands	ip, r2, #3
+		moveq	r0, r3
+		beq	alldone_ba
+		mov	r5, ip, lsl #3
+		rsb	r5, r5, #32
+		ldr	r4, [r0]
+		mov	r6, r4, lsl r5
+		adds	r3, r3, r6, lsr r5
+		adc	r0, r3, #0
+		strb	r4, [r1], #1
+		tst	ip, #2
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		mov	r4, r4, lsr #8
+		strb	r4, [r1], #1
+		teq	ip, #3
+		moveq	r4, r4, lsr #8
+		streqb	r4, [r1], #1
+alldone_ba:	ldmea	fp, {r4 - r10, fp, sp, pc}^
+
+src_not_aligned:
+		cmp	r2, #4
+		bic	r0, r0, #3
+		ldrcst	r4, [r0], #4
+		adds	r3, r3, #0
+		bics	ip, r2, #31
+		beq	notlots32_b
+lp32_b:		ldrt	r5, [r0], #4
+		ldrt	r6, [r0], #4
+		ldrt	r7, [r0], #4
+		ldrt	r8, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, r5, lsr #16
+		orr	r5, r5, r6, lsl #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r7, lsl #16
+		mov	r7, r7, lsr #16
+		orr	r7, r7, r8, lsl #16
+		stmia	r1!, {r4, r5, r6, r7}
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		mov	r4, r8
+		ldrt	r5, [r0], #4
+		ldrt	r6, [r0], #4
+		ldrt	r7, [r0], #4
+		ldrt	r8, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, r5, lsr #16
+		orr	r5, r5, r6, lsl #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r7, lsl #16
+		mov	r7, r7, lsr #16
+		orr	r7, r7, r8, lsl #16
+		stmia	r1!, {r4, r5, r6, r7}
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		mov	r4, r8
+		sub	ip, ip, #32
+		teq	ip, #0
+		bne	lp32_b
+		adcs	r3, r3, #0
+notlots32_b:	ands	ip, r2, #0x1c
+		beq	nobits32_b
+bits32lp_b:	ldrt	r5, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		str	r4, [r1], #4
+		adcs	r3, r3, r4
+		mov	r4, r5
+		sub	ip, ip, #4
+		teq	ip, #0
+		bne	bits32lp_b
+		adc	r3, r3, #0
+nobits32_b:	ands	ip, r2, #3
+		moveq	r0, r3
+		beq	alldone_b
+		ldrt	r5, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, ip, lsl #3
+		rsb	r5, r5, #32
+		mov	r6, r4, lsl r5
+		adds	r3, r3, r6, lsr r5
+		adc	r0, r3, #0
+		strb	r4, [r1], #1
+		tst	ip, #2
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		mov	r4, r4, lsr #8
+		strb	r4, [r1], #1
+		teq	ip, #3
+		moveq	r4, r4, lsr #8
+		streqb	r4, [r1], #1
+alldone_b:	ldmea	fp, {r4 - r10, fp, sp, pc}^
+
+
+@ r0 = src
+@ r1 = dst
+@ r2 = len
+@ r3 = sum
+		.global	_csum_partial_copy
+_csum_partial_copy:
+		mov	ip, sp
+		stmfd	sp!, {r4 - r10, fp, ip, lr, pc}
+		sub	fp, ip, #4
+		tst	r1, #2			@ Test destination alignment
+		beq	dst_aligned_2
+		subs	r2, r2, #2
+		addmi	r2, r2, #2
+		bmi	nobits32_b_2
+		ldr	r4, [r0], #2
+		mov	r5, r4, lsl #16
+		adds	r3, r3, r5, lsr #16
+		adcs	r3, r3, #0
+		strb	r4, [r1], #1
+		mov	r4, r4, lsr #8
+		strb	r4, [r1], #1		@ Destination now aligned
+dst_aligned_2:	tst	r0, #2
+		bne	src_not_aligned_2
+		adds	r3, r3, #0
+		bics	ip, r2, #31		@ Routine for src & dst aligned
+		beq	nolots32_ba_2
+lp32_ba_2:	ldr	r4, [r0], #4
+		ldr	r5, [r0], #4
+		ldr	r6, [r0], #4
+		ldr	r7, [r0], #4
+		str	r4, [r1], #4
+		str	r5, [r1], #4
+		str	r6, [r1], #4
+		str	r7, [r1], #4
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		ldr	r4, [r0], #4
+		ldr	r5, [r0], #4
+		ldr	r6, [r0], #4
+		ldr	r7, [r0], #4
+		str	r4, [r1], #4
+		str	r5, [r1], #4
+		str	r6, [r1], #4
+		str	r7, [r1], #4
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		sub	ip, ip, #32
+		teq	ip, #0
+		bne	lp32_ba_2
+		adcs	r3, r3, #0
+nolots32_ba_2:	ands	ip, r2, #0x1c
+		beq	nobits32_ba_2
+bits32lp_ba_2:	ldr	r4, [r0], #4
+		str	r4, [r1], #4
+		adcs	r3, r3, r4
+		sub	ip, ip, #4
+		teq	ip, #0
+		bne	bits32lp_ba_2
+		adcs	r3, r3, #0
+nobits32_ba_2:	ands	ip, r2, #3
+		moveq	r0, r3
+		beq	alldone_ba_2
+		mov	r5, ip, lsl #3
+		rsb	r5, r5, #32
+		ldr	r4, [r0]
+		mov	r6, r4, lsl r5
+		adds	r3, r3, r6, lsr r5
+		adcs	r0, r3, #0
+		strb	r4, [r1], #1
+		tst	ip, #2
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		mov	r4, r4, lsr #8
+		strb	r4, [r1], #1
+		teq	ip, #3
+		moveq	r4, r4, lsr #8
+		streqb	r4, [r1], #1
+alldone_ba_2:	ldmea	fp, {r4 - r10, fp, sp, pc}^
+
+src_not_aligned_2:
+		cmp	r2, #4
+		bic	r0, r0, #3
+		ldrcs	r4, [r0], #4
+		adds	r3, r3, #0
+		bics	ip, r2, #31
+		beq	notlots32_b_2
+lp32_b_2:	ldr	r5, [r0], #4
+		ldr	r6, [r0], #4
+		ldr	r7, [r0], #4
+		ldr	r8, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, r5, lsr #16
+		orr	r5, r5, r6, lsl #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r7, lsl #16
+		mov	r7, r7, lsr #16
+		orr	r7, r7, r8, lsl #16
+		stmia	r1!, {r4, r5, r6, r7}
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		mov	r4, r8
+		ldr	r5, [r0], #4
+		ldr	r6, [r0], #4
+		ldr	r7, [r0], #4
+		ldr	r8, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, r5, lsr #16
+		orr	r5, r5, r6, lsl #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r7, lsl #16
+		mov	r7, r7, lsr #16
+		orr	r7, r7, r8, lsl #16
+		stmia	r1!, {r4, r5, r6, r7}
+		adcs	r3, r3, r4
+		adcs	r3, r3, r5
+		adcs	r3, r3, r6
+		adcs	r3, r3, r7
+		mov	r4, r8
+		sub	ip, ip, #32
+		teq	ip, #0
+		bne	lp32_b_2
+		adcs	r3, r3, #0
+notlots32_b_2:	ands	ip, r2, #0x1c
+		beq	nobits32_b_2
+bits32lp_b_2:	ldr	r5, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		str	r4, [r1], #4
+		adcs	r3, r3, r4
+		mov	r4, r5
+		sub	ip, ip, #4
+		teq	ip, #0
+		bne	bits32lp_b_2
+		adcs	r3, r3, #0
+nobits32_b_2:	ands	ip, r2, #3
+		moveq	r0, r3
+		beq	alldone_b_2
+		ldr	r5, [r0], #4
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, ip, lsl #3
+		rsb	r5, r5, #32
+		mov	r6, r4, lsl r5
+		adds	r3, r3, r6, lsr r5
+		adcs	r0, r3, #0
+		strb	r4, [r1], #1
+		tst	ip, #2
+		ldmeqea	fp, {r4 - r10, fp, sp, pc}^
+		mov	r4, r4, lsr #8
+		strb	r4, [r1], #1
+		teq	ip, #3
+		moveq	r4, r4, lsr #8
+		streqb	r4, [r1], #1
+alldone_b_2:	ldmea	fp, {r4 - r10, fp, sp, pc}^
+
+
+
diff -urNwbB linux/arch/arm/lib/irqs.S linux.arm/arch/arm/lib/irqs.S
--- linux/arch/arm/lib/irqs.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/irqs.S	Sun Mar  3 13:14:55 1996
@@ -0,0 +1,229 @@
+/*
+ * linux/arch/arm/lib/irqs.S
+ *
+ * Copyright (C) 1995, 1996 Russell King. (rmk92@ecs.soton.ac.uk)
+ *
+ * Interrupt wrappers - handles disabling & re-enabling interrupts
+ *
+ * Changes:
+ *  09/02/1996	Stream-lined normal interrupt wrapper - now uses 78
+ *		cycles instead of 85 and 3 less instructions.
+ */
+
+#include <asm/assembler.h>
+
+@ IRQ stubs
+
+@ IRQ stubs entered with:
+@ r1 = IOC
+@ r2 = IRQ number
+@ return 0 for normal irq
+
+		.text
+#define BAD_IRQ8(n, v1, v2)				\
+		.global	_bad_IRQ##n##_interrupt		;\
+_bad_IRQ##n##_interrupt:				;\
+		stmfd	sp!, {lr}			;\
+		ldrb	r0, [r4, \#0x18]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x18]		;\
+		bl	_bad_IRQ			;\
+		mov	r0, \#1				;\
+		LOADREGS(fd, sp!, {pc})
+
+#define BAD_IRQ16(n, v1, v2)				\
+		.global	_bad_IRQ##n##_interrupt		;\
+_bad_IRQ##n##_interrupt:				;\
+		stmfd	sp!, {lr}			;\
+		ldrb	r0, [r4, \#0x28]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x28]		;\
+		bl	_bad_IRQ			;\
+		mov	r0, \#1				;\
+		LOADREGS(fd, sp!, {pc})
+
+BAD_IRQ8 ( 0,   1,    1)
+BAD_IRQ8 ( 1,   2,    2)
+BAD_IRQ8 ( 2,   4,    4)
+BAD_IRQ8 ( 3,   8,    8)
+BAD_IRQ8 ( 4,  16,   16)
+BAD_IRQ8 ( 5,  32,   32)
+BAD_IRQ8 ( 6,  64,   64)
+BAD_IRQ8 ( 7, 128,  128)
+BAD_IRQ16( 8,   1,  256)
+BAD_IRQ16( 9,   2,  512)
+BAD_IRQ16(10,   4, 1024)
+BAD_IRQ16(11,   8, 2048)
+BAD_IRQ16(12,  16, 4096)
+BAD_IRQ16(13,  32, 8192)
+BAD_IRQ16(14,  64,16384)
+BAD_IRQ16(15, 128,32768)
+
+#define FAST_IRQ8(n, v1, v2)				\
+		.global	_fast_IRQ##n##_interrupt	;\
+_fast_IRQ##n##_interrupt:				;\
+		stmfd	sp!, {lr}			;\
+		adr	r5, LC1				;\
+		ldmia	r5, {r5, r6, r7}		;\
+		ldrb	r0, [r4, \#0x18]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x18]		;\
+		mov	r0, \# v1			;\
+		strb	r0, [r4, \#0x14]		;\
+		ldrb	r8, [r7]			;\
+		orr	r0, r8, \# v1			;\
+		strb	r0, [r7]			;\
+		ldr	r9, [r5]			;\
+		add	r0, r9, \#1			;\
+		str	r0, [r5]			;\
+		mov	r0, \# n			;\
+		bl	_do_IRQ				;\
+		DISABLEIRQS				;\
+		str	r9, [r5]			;\
+		strb	r8, [r7]			;\
+		ldrb	r0, [r6]			;\
+		tst	r0, \# v1			;\
+		ldrneb	r0, [r4, \#0x18]		;\
+		orrne	r0, r0, \# v1			;\
+		strneb	r0, [r4, \#0x18]		;\
+		mov	r0, \#1				;\
+		LOADREGS(fd, sp!, {pc})
+
+#define FAST_IRQ16(n, v1, v2)				\
+		.global	_fast_IRQ##n##_interrupt	;\
+_fast_IRQ##n##_interrupt:				;\
+		stmfd	sp!, {lr}			;\
+		adr	r5, LC1				;\
+		ldmia	r5, {r5, r6, r7}		;\
+		ldrb	r0, [r4, \#0x28]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x28]		;\
+		ldrb	r8, [r7, \#1]			;\
+		orr	r0, r8, \# v1			;\
+		strb	r0, [r7, \#1]			;\
+		ldr	r9, [r5]			;\
+		add	r0, r9, \#1			;\
+		str	r0, [r5]			;\
+		mov	r0, \# n			;\
+		bl	_do_IRQ				;\
+		DISABLEIRQS				;\
+		str	r9, [r5]			;\
+		strb	r8, [r7, \#1]			;\
+		ldrb	r0, [r6, \#1]			;\
+		tst	r0, \# v1			;\
+		ldrneb	r0, [r4, \#0x28]		;\
+		orrne	r0, r0, \# v1			;\
+		strneb	r0, [r4, \#0x28]		;\
+		mov	r0, \#1				;\
+		LOADREGS(fd, sp!, {pc})
+
+FAST_IRQ8 ( 0,   1,    1)
+FAST_IRQ8 ( 1,   2,    2)
+FAST_IRQ8 ( 2,   4,    4)
+FAST_IRQ8 ( 3,   8,    8)
+FAST_IRQ8 ( 4,  16,   16)
+FAST_IRQ8 ( 5,  32,   32)
+FAST_IRQ8 ( 6,  64,   64)
+FAST_IRQ8 ( 7, 128,  128)
+LC1:		.word	_intr_count
+		.word	_enabled_irqs
+		.word	_executing_irqs
+
+FAST_IRQ16( 8,   1,  256)
+FAST_IRQ16( 9,   2,  512)
+FAST_IRQ16(10,   4, 1024)
+FAST_IRQ16(11,   8, 2048)
+FAST_IRQ16(12,  16, 4096)
+FAST_IRQ16(13,  32, 8192)
+FAST_IRQ16(14,  64,16384)
+FAST_IRQ16(15, 128,32768)
+
+#define NORM_IRQ8(n, v1, v2)				\
+		.global	_IRQ##n##_interrupt		;\
+_IRQ##n##_interrupt:					;\
+		stmfd	sp!, {lr}			;\
+		adr	r5, LC2				;\
+		ldmia	r5, {r5, r6, r7}		;\
+		ldrb	r0, [r4, \#0x18]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x18]		;\
+		mov	r0, \# v1			;\
+		strb	r0, [r4, \#0x14]		;\
+		ldrb	r8, [r7]			;\
+		orr	r0, r8, \# v1			;\
+		strb	r0, [r7]			;\
+		ldr	r9, [r5]			;\
+		add	r0, r9, \#1			;\
+		str	r0, [r5]			;\
+		ENABLEIRQS				;\
+		mov	r0, \# n			;\
+		bl	_do_IRQ				;\
+		DISABLEIRQS				;\
+		str	r9, [r5]			;\
+		strb	r8, [r7]			;\
+		ldrb	r0, [r6]			;\
+		tst	r0, \# v1			;\
+		ldrneb	r0, [r4, \#0x18]		;\
+		orrne	r0, r0, \# v1			;\
+		strneb	r0, [r4, \#0x18]		;\
+		mov	r0, \#0				;\
+		LOADREGS(fd, sp!, {pc})
+
+#define NORM_IRQ16(n, v1, v2)				\
+		.global	_IRQ##n##_interrupt		;\
+_IRQ##n##_interrupt:					;\
+		stmfd	sp!, {lr}			;\
+		adr	r5, LC2				;\
+		ldmia	r5, {r5, r6, r7}		;\
+		ldrb	r0, [r4, \#0x28]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x28]		;\
+		ldrb	r8, [r7, \#1]			;\
+		orr	r0, r8, \# v1			;\
+		strb	r0, [r7, \#1]			;\
+		ldr	r9, [r5]			;\
+		add	r0, r9, \#1			;\
+		str	r0, [r5]			;\
+		ENABLEIRQS				;\
+		mov	r0, \# n			;\
+		bl	_do_IRQ				;\
+		DISABLEIRQS				;\
+		str	r9, [r5]			;\
+		strb	r8, [r7, \#1]			;\
+		ldrb	r0, [r6, \#1]			;\
+		tst	r0, \# v1			;\
+		ldrneb	r0, [r4, \#0x28]		;\
+		orrne	r0, r0, \# v1			;\
+		strneb	r0, [r4, \#0x28]		;\
+		mov	r0, \#0				;\
+		LOADREGS(fd, sp!, {pc})
+
+NORM_IRQ8 ( 0,   1,    1)
+NORM_IRQ8 ( 1,   2,    2)
+NORM_IRQ8 ( 2,   4,    4)
+NORM_IRQ8 ( 3,   8,    8)
+NORM_IRQ8 ( 4,  16,   16)
+NORM_IRQ8 ( 5,  32,   32)
+NORM_IRQ8 ( 6,  64,   64)
+NORM_IRQ8 ( 7, 128,  128)
+LC2:		.word	_intr_count
+		.word	_enabled_irqs
+		.word	_executing_irqs
+
+NORM_IRQ16( 8,   1,  256)
+NORM_IRQ16( 9,   2,  512)
+NORM_IRQ16(10,   4, 1024)
+NORM_IRQ16(11,   8, 2048)
+NORM_IRQ16(12,  16, 4096)
+NORM_IRQ16(13,  32, 8192)
+NORM_IRQ16(14,  64,16384)
+NORM_IRQ16(15, 128,32768)
+
+		.global	_bad_IRQ
+
+_bad_IRQ:	adr	r0, msg
+		mov	r1, r2
+		b	_printk
+
+msg:		.ascii	"Bad interrupt %d received!\n\0"
+		.align
diff -urNwbB linux/arch/arm/lib/ll_char_wr.S linux.arm/arch/arm/lib/ll_char_wr.S
--- linux/arch/arm/lib/ll_char_wr.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/ll_char_wr.S	Sun Mar  3 13:15:26 1996
@@ -0,0 +1,129 @@
+/*
+ * linux/arch/arm/lib/ll_char_wr.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+
+		.global	_ll_char_write
+
+#define BOLD            0x01
+#define ITALIC          0x02
+#define UNDERLINE       0x04
+#define FLASH           0x08
+#define INVERSE         0x10
+
+@ r4 = flags
+@ r3 = backc
+@ r2 = forec
+@ r1 = ch
+@ r0 = ps
+
+LC0:
+		.word	_bytes_per_char_h
+		.word	_video_num_columns
+		.word	_cmap_80
+
+_ll_char_write:
+		subs	r1, r1, #32
+		movlt	r1, #0
+		cmp	r1, #126-32
+		RETINSTR(movgt,pc,lr)
+		mov	ip, sp
+		stmfd	sp!, {r4 - r9, fp, ip, lr, pc}
+		sub	fp, ip, #4
+@ do inverse stuff
+		ldr	r4, [fp, #4]		@ get flags
+		eor	r4, r4, #UNDERLINE
+		tst	r4, #INVERSE
+		movne	r5, r3
+		movne	r3, r2
+		movne	r2, r5
+		tst	r4, #BOLD
+		orrne	r2, r2, #8
+@ compute the offset required for each row
+		ldr	r5, [pc, #LC0 - . - 8]
+		ldrb	r5, [r5]
+		ldr	r6, [pc, #LC0 - . - 4]
+		ldrb	r6, [r6]
+		mul	r6, r5, r6
+		ldr	r7, [pc, #LC0 - .]
+		mov	r1, r1, lsl #3
+		teq	r5, #8
+		beq	row8bpplp
+row4bpplp:
+		ldrb	r9, [r7, r1]
+		and	r8, r1, #7
+		teq	r8, #7
+		tsteq	r4, #UNDERLINE
+		mvneq	r9, r9
+		sub	r8, r5, #1
+		mov	ip, #0
+		tst	r9, #1<<7
+		orreq	ip, ip, r3
+		orrne	ip, ip, r2
+		tst	r9, #1<<6
+		orreq	ip, ip, r3, lsl#4
+		orrne	ip, ip, r2, lsl#4
+		tst	r9, #1<<5
+		orreq	ip, ip, r3, lsl#8
+		orrne	ip, ip, r2, lsl#8
+		tst	r9, #1<<4
+		orreq	ip, ip, r3, lsl#12
+		orrne	ip, ip, r2, lsl#12
+		tst	r9, #1<<3
+		orreq	ip, ip, r3, lsl#16
+		orrne	ip, ip, r2, lsl#16
+		tst	r9, #1<<2
+		orreq	ip, ip, r3, lsl#20
+		orrne	ip, ip, r2, lsl#20
+		tst	r9, #1<<1
+		orreq	ip, ip, r3, lsl#24
+		orrne	ip, ip, r2, lsl#24
+		tst	r9, #1<<0
+		orreq	ip, ip, r3, lsl#28
+		orrne	ip, ip, r2, lsl#28
+		str	ip, [r0],r6
+		add	r1, r1,#1
+		tst	r1, #7
+		bne	row4bpplp
+		LOADREGS(ea, fp, {r4 - r9, fp, sp, pc})
+
+row8bpplp:	ldrb	r9, [r7, r1]
+		and	r8, r1, #7
+		teq	r8, #7
+		tsteq	r4, #UNDERLINE
+		mvneq	r9, r9
+		tst	r9, #1<<7
+		moveq	r5, r3
+		movne	r5, r2
+		tst	r9, #1<<6
+		orreq	r5, r5, r3, lsl #8
+		orrne	r5, r5, r2, lsl #8
+		tst	r9, #1<<5
+		orreq	r5, r5, r3, lsl #16
+		orrne	r5, r5, r2, lsl #16
+		tst	r9, #1<<4
+		orreq	r5, r5, r3, lsl #24
+		orrne	r5, r5, r2, lsl #24
+		tst	r9, #1<<3
+		moveq	ip, r3
+		movne	ip, r2
+		tst	r9, #1<<2
+		orreq	ip, ip, r3, lsl #8
+		orrne	ip, ip, r2, lsl #8
+		tst	r9, #1<<1
+		orreq	ip, ip, r3, lsl #16
+		orrne	ip, ip, r2, lsl #16
+		tst	r9, #1<<0
+		orreq	ip, ip, r3, lsl #24
+		orrne	ip, ip, r2, lsl #24
+		stmia	r0, {r5, ip}
+		add	r0, r0, r6
+		add	r1,r1,#1
+		tst	r1,#7
+		bne	row8bpplp
+		LOADREGS(ea, fp, {r4 - r9, fp, sp, pc})
+
diff -urNwbB linux/arch/arm/lib/memfastset.S linux.arm/arch/arm/lib/memfastset.S
--- linux/arch/arm/lib/memfastset.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/memfastset.S	Sun Mar  3 13:15:54 1996
@@ -0,0 +1,40 @@
+/*
+ * linux/arch/arm/lib/memfastset.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+@ Prototype: void memfastset (void *d, unsigned long c, size_t n);
+
+		.global	_memfastset
+
+_memfastset:	stmfd	sp!, {lr}
+		subs	r2, r2, #32
+		bmi	memfastl32
+memfast32setlp:	str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		LOADREGS(eqfd, sp!, {pc})
+		subs	r2, r2, #32
+		bpl	memfast32setlp
+memfastl32:	adds	r2, r2, #16
+		bmi	memfastl16
+memfast16setlp:	str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		str	r1, [r0], #4
+		LOADREGS(eqfd, sp!, {pc})
+		subs	r2, r2, #16
+		bpl	memfast16setlp
+memfastl16:	add	r2, r2, #16
+memfastsetlp:	subs	r2, r2, #4
+		strge	r1, [r0], #4
+		bgt	memfastsetlp
+		LOADREGS(fd, sp!, {pc})
diff -urNwbB linux/arch/arm/lib/prints.S linux.arm/arch/arm/lib/prints.S
--- linux/arch/arm/lib/prints.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/prints.S	Sun Mar  3 13:16:21 1996
@@ -0,0 +1,58 @@
+/* 
+ * linux/arch/arm/lib/prints.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+
+		.global	_vprints
+_vprints:	mov	ip, sp
+		stmfd	sp!, {r0-r3}
+		stmfd	sp!, {fp, ip, lr, pc}
+		sub	fp, ip, #4+4*4
+		sub	sp, sp, #4
+		add	r2, fp, #8
+		mov	r1, r0
+		sub	sp, sp, #2048
+		mov	r0, sp
+		bl	_vsprintf
+		mov	r0, sp
+		mov	r2, #0x03000000
+		orr	r2, r2, #0x00010000
+		orr	r2, r2, #0x00000fe0
+outlp:		ldrb	r1, [r0], #1
+		teq	r1, #0
+		beq	out
+		teq	r1, #10
+		moveq	r3, #13
+		streqb	r3, [r0, #-1]!
+		strb	r1, [r2]
+		mov	r3, #0x8000
+dlylp:		sub	r3, r3, #1
+		teq	r3, #0
+		bne	dlylp
+		b	outlp
+out:		LOADREGS(ea, fp, {fp, sp, pc})
+
+		.global	_prints
+_prints:	mov	ip, sp
+		stmfd	sp!, {fp, ip, lr, pc}
+		SUB	fp, ip, #4
+		mov	r2, #0x03000000
+		orr	r2, r2, #0x00010000
+		orr	r2, r2, #0x00000fe0
+out2lp:		LDRB	r1, [r0], #1
+		TEQ	r1, #0
+		BEQ	out
+		TEQ	r1, #10
+		MOVEQ	r3, #13
+		STREQB	r3, [r0, #-1]!
+		STRB	r1, [r2]
+		MOV	r3, #0x8000
+dly2lp:		SUB	r3, r3, #1
+		TEQ	r3, #0
+		BNE	dly2lp
+		B	out2lp
+
diff -urNwbB linux/arch/arm/lib/segment.S linux.arm/arch/arm/lib/segment.S
--- linux/arch/arm/lib/segment.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/segment.S	Sun Mar  3 13:20:42 1996
@@ -0,0 +1,599 @@
+/*
+ * linux/arch/arm/lib/segment.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ * Except memcpy/memmove routine.
+ */
+
+#include <asm/assembler.h>
+		.text
+
+# Prototype: unsigned char get_user_byte(const char *addr)
+
+		.global	_get_user_byte
+_get_user_byte:	stmfd	sp!, {lr}
+		ldrbt	r0, [r0], #0
+		LOADREGS(fd, sp!, {pc})
+
+# Prototype: void put_user_byte(char c,const char *addr)
+
+		.global	_put_user_byte
+_put_user_byte:	stmfd	sp!, {lr}
+		strbt	r0, [r1], #0
+		LOADREGS(fd, sp!, {pc})
+
+# Prototype: unsigned short get_user_word(const short *addr)
+
+		.global	_get_user_word
+_get_user_word:	stmfd	sp!, {lr}
+		ldrbt	r1, [r0], #1
+		ldrbt	r0, [r0], #0
+		orr	r0, r1, r0, lsl #8
+		LOADREGS(fd, sp!, {pc})
+
+# Prototype: void put_user_word(short val,const short *addr)
+
+		.global	_put_user_word
+_put_user_word:	stmfd	sp!, {lr}
+		mov	r2, r0, lsr #8
+		strbt	r0, [r1], #1
+		strbt	r2, [r1], #0
+		LOADREGS(fd, sp!, {pc})
+
+# Prototype: unsigned long get_user_long(const long *addr)
+
+		.global	_get_user_long
+_get_user_long:	stmfd	sp!, {lr}
+		ands	r2, r0, #3
+		bic	r1, r0, #3
+		ldrt	r0, [r1], #4
+		LOADREGS(eqfd, sp!, {pc})
+		ldrt	r3, [r1], #0
+		mov	r2, r2, lsl #3
+		mov	r0, r0, lsr r2
+		rsb	r2, r2, #32
+		orr	r0, r0, r3, lsl r2
+		LOADREGS(fd, sp!, {pc})
+
+# Prototype: void put_user_byte(long val,const long *addr)
+
+		.global	_put_user_long
+_put_user_long:	stmfd	sp!, {lr}
+		ands	r2, r1, #3
+		streqt	r0, [r1], #0
+		ldmeqfd	sp!, {pc}^
+		strbt	r0, [r1], #1
+		mov	r0, r0,lsr #8
+		strbt	r0, [r1], #1
+		mov	r0, r0,lsr #8
+		strbt	r0, [r1], #1
+		mov	r0, r0, lsr #8
+		strbt	r0, [r1], #0
+		LOADREGS(fd, sp!, {pc})
+
+#define ENTER	\
+		MOV	ip,sp	;\
+		STMFD	sp!,{r4-r9,fp,ip,lr,pc}	;\
+		SUB	fp,ip,#4
+
+#define EXIT	\
+		LOADREGS(ea, fp, {r4 - r9, fp, sp, pc})
+
+#define EXITEQ	\
+		LOADREGS(eqea, fp, {r4 - r9, fp, sp, pc})
+
+# Prototype: void memcpy_tofs(void *to,const void *from,unsigned long n);
+
+		.global	___memcpy_tofs
+___memcpy_tofs:
+		cmp	r0, #0x02000000			@ PHYS check
+		bge	_memcpy_fromfs
+		ENTER
+		subs	r2, r2, #4
+		blt	no_double_words
+		ands	ip, r0, #3
+		bne	dest_not_aligned
+		ands	ip, r1, #3
+		bne	src_not_aligned
+rest:		subs	r2, r2, #8
+		blt	cpy_2_lp
+		subs	r2, r2, #0x14
+		blt	not_long_copy
+
+cpy_8_lp:	ldmia	r1!, {r3 - r9, ip}
+		strt	r3, [r0], #4
+		strt	r4, [r0], #4
+		strt	r5, [r0], #4
+		strt	r6, [r0], #4
+		strt	r7, [r0], #4
+		strt	r8, [r0], #4
+		strt	r9, [r0], #4
+		strt	ip, [r0], #4
+		subs	r2, r2, #32
+		bge	cpy_8_lp
+		cmn	r2,#16
+		ldmgeia	r1!,{r3 - r6}
+		strget	r3, [r0], #4
+		strget	r4, [r0], #4
+		strget	r5, [r0], #4
+		strget	r6, [r0], #4
+		subge	r2, r2, #0x10
+
+not_long_copy:	adds	r2,r2,#0x14
+
+cpy_3_lp:	ldmgeia	r1!, {r3 - r5}
+		strget	r3, [r0], #4
+		strget	r4, [r0], #4
+		strget	r5, [r0], #4
+		subges	r2, r2, #12
+		bge	cpy_3_lp
+
+cpy_2_lp:	adds	r2, r2, #8
+		blt	no_double_words
+		subs	r2, r2, #4
+		ldrlt	r3, [r1], #4
+		strltt	r3, [r0], #4
+		ldmgeia	r1!,{r3, r4}
+		strget	r3, [r0], #4
+		strget	r4, [r0], #4
+		subge	r2, r2, #4
+
+no_double_words:
+		adds	r2, r2, #4
+		EXITEQ
+		cmp	r2, #2
+		ldrb	r3, [r1], #1
+		strbt	r3, [r0], #1
+		ldrgeb	r3, [r1], #1
+		strgebt	r3, [r0], #1
+		ldrgtb	r3, [r1], #1
+		strgtbt	r3, [r0], #1
+		EXIT
+dest_not_aligned:
+		rsb	ip, ip, #4
+		cmp	ip, #2
+		ldrb	r3, [r1], #1
+		strbt	r3, [r0], #1
+		ldrgeb	r3, [r1], #1
+		strgebt	r3, [r0], #1
+		ldrgtb	r3, [r1], #1
+		strgtbt	r3, [r0], #1
+		subs	r2, r2, ip
+		blt	no_double_words
+		ands	ip, r1, #3
+		beq	rest
+src_not_aligned:
+		bic	r1, r1, #3
+		ldr	r7, [r1], #4
+		cmp	ip, #2
+		bgt	cpy_4_3
+		beq	cpy_4_2
+		cmp	r2, #12
+		blt	cpy_x_1_lp
+		sub	r2, r2, #12
+
+cpy_4_1_lp:	mov	r3, r7, lsr #8
+		ldmia	r1!, {r4 - r7}
+		orr	r3, r3, r4, lsl #24
+		mov	r4, r4, lsr #8
+		orr	r4, r4, r5, lsl #24
+		mov	r5, r5, lsr #8
+		orr	r5, r5, r6, lsl #24
+		mov	r6, r6, lsr #8
+		orr	r6, r6, r7, lsl #24
+		strt	r3, [r0], #4
+		strt	r4, [r0], #4
+		strt	r5, [r0], #4
+		strt	r6, [r0], #4
+		subs	r2, r2, #16
+		bge	cpy_4_1_lp
+		adds	r2,r2,#12
+		blt	rest1
+
+cpy_x_1_lp:	mov	r3, r7, lsr #8
+		ldr	r7, [r1], #4
+		orr	r3, r3, r7, lsl #24
+		strt	r3, [r0], #4
+		subs	r2, r2, #4
+		bge	cpy_x_1_lp
+
+rest1:		sub	r1, r1, #3
+		b	no_double_words
+
+cpy_4_2:	cmp	r2, #12
+		blt	cpy_x_2_lp
+		sub	r2, r2, #12
+
+cpy_4_2_lp:	mov	r3, r7, lsr #16
+		ldmia	r1!, {r4 - r7}
+		orr	r3, r3, r4, lsl #16
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, r5, lsr #16
+		orr	r5, r5, r6, lsl #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r7, lsl #16
+		strt	r3, [r0], #4
+		strt	r4, [r0], #4
+		strt	r5, [r0], #4
+		strt	r6, [r0], #4
+		subs	r2, r2, #16
+		bge	cpy_4_2_lp
+		adds	r2, r2, #12
+		blt	rest2
+
+cpy_x_2_lp:	mov	r3, r7, lsr #16
+		ldr	r7, [r1], #4
+		orr	r3, r3, r7, lsl #16
+		strt	r3, [r0], #4
+		subs	r2, r2, #4
+		bge	cpy_x_2_lp
+
+rest2:		sub	r1, r1, #2
+		b	no_double_words
+
+cpy_4_3:	cmp	r2, #12
+		blt	cpy_x_3_lp
+		sub	r2, r2, #12
+
+cpy_4_3_lp:	mov	r3, r7, lsr #24
+		ldmia	r1!, {r4 - r7}
+		orr	r3, r3, r4, lsl #8
+		mov	r4, r4, lsr #24
+		orr	r4, r4, r5, lsl #8
+		mov	r5, r5, lsr #24
+		orr	r5, r5, r6, lsl #8
+		mov	r6, r6, lsr #24
+		orr	r6, r6, r7, lsl #8
+		strt	r3, [r0], #4
+		strt	r4, [r0], #4
+		strt	r5, [r0], #4
+		strt	r6, [r0], #4
+		subs	r2, r2, #16
+		bge	cpy_4_3_lp
+		adds	r2, r2, #12
+		blt	rest3
+
+cpy_x_3_lp:	mov	r3, r7, lsr #24
+		ldr	r7, [r1], #4
+		orr	r3, r3, r7, lsl #8
+		strt	r3, [r0], #4
+		subs	r2, r2, #4
+		bge	cpy_x_3_lp
+
+rest3:		sub	r1, r1, #1
+		b	no_double_words
+
+# Prototype: void memcpy_fromfs(void *to,const void *from,unsigned long n);
+# ARM3: cant use memcopy here!!!
+
+		.global	_memcpy
+		.global	_memmove
+		.global	_memcpy_fromfs
+_memcpy:
+_memmove:
+_memcpy_fromfs:
+		ENTER
+		cmp	r1, r0
+		bcc	other_copy
+		subs	r2, r2, #4
+		blt	up_no_double_words
+		ands	ip, r0, #3
+		bne	up_dest_not_aligned
+		ands	ip, r1, #3
+		bne	up_src_not_aligned
+
+up_rest:	subs	r2, r2, #8
+		blt	up_cpy_2_lp
+		subs	r2, r2, #0x14
+		blt	up_not_long_copy
+
+up_cpy_8_lp:	ldmia	r1!,{r3 - r9, ip}
+		stmia	r0!,{r3 - r9, ip}
+		subs	r2, r2, #32
+		bge	up_cpy_8_lp
+		cmn	r2, #16
+		ldmgeia	r1!, {r3 - r6}
+		stmgeia	r0!, {r3 - r6}
+		subge	r2, r2, #0x10
+up_not_long_copy:
+		adds	r2, r2, #0x14
+
+up_cpy_3_lp:	ldmgeia	r1!, {r3 - r5}
+		stmgeia	r0!, {r3 - r5}
+		subges	r2, r2, #12
+		bge	up_cpy_3_lp
+
+up_cpy_2_lp:	adds	r2, r2, #8
+		blt	up_no_double_words
+		subs	r2, r2, #4
+		ldrlt	r3, [r1], #4
+		strlt	r3, [r0], #4
+		ldmgeia	r1!, {r3, r4}
+		stmgeia	r0!, {r3, r4}
+		subge	r2, r2, #4
+
+up_no_double_words:
+		adds	r2, r2, #4
+		EXITEQ
+		cmp	r2, #2
+		ldrb	r3, [r1], #1
+		strb	r3, [r0], #1
+		ldrgeb	r3, [r1], #1
+		strgeb	r3, [r0], #1
+		ldrgtb	r3, [r1], #1
+		strgtb	r3, [r0], #1
+		EXIT
+up_dest_not_aligned:
+		rsb	ip, ip, #4
+		cmp	ip, #2
+		ldrb	r3, [r1], #1
+		strb	r3, [r0], #1
+		ldrgeb	r3, [r1], #1
+		strgeb	r3, [r0], #1
+		ldrgtb	r3, [r1], #1
+		strgtb	r3, [r0], #1
+		subs	r2, r2, ip
+		blt	up_no_double_words
+		ands	ip, r1, #3
+		beq	up_rest
+up_src_not_aligned:
+		bic	r1, r1, #3
+		ldr	r7, [r1], #4
+		cmp	ip, #2
+		bgt	up_cpy_4_3
+		beq	up_cpy_4_2
+		cmp	r2, #12
+		blt	up_cpy_x_1_lp
+		sub	r2, r2, #12
+
+up_cpy_4_1_lp:	mov	r3, r7, lsr #8
+		ldmia	r1!, {r4 - r7}
+		orr	r3, r3, r4, lsl #24
+		mov	r4, r4, lsr #8
+		orr	r4, r4, r5, lsl #24
+		mov	r5, r5, lsr #8
+		orr	r5, r5, r6, lsl #24
+		mov	r6, r6, lsr #8
+		orr	r6, r6, r7, lsl #24
+		stmia	r0!, {r3 - r6}
+		subs	r2, r2, #16
+		bge	up_cpy_4_1_lp
+		adds	r2, r2, #12
+		blt	up_rest1
+
+up_cpy_x_1_lp:	mov	r3, r7, lsr #8
+		ldr	r7, [r1], #4
+		orr	r3, r3, r7, lsl #24
+		str	r3, [r0], #4
+		subs	r2, r2, #4
+		bge	up_cpy_x_1_lp
+
+up_rest1:	sub	r1, r1, #3
+		b	up_no_double_words
+
+up_cpy_4_2:	cmp	r2, #12
+		blt	up_cpy_x_2_lp
+		sub	r2, r2, #12
+
+up_cpy_4_2_lp:	mov	r3, r7, lsr #16
+		ldmia	r1!, {r4 - r7}
+		orr	r3, r3, r4, lsl #16
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r5, lsl #16
+		mov	r5, r5, lsr #16
+		orr	r5, r5, r6, lsl #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r7,LSL#16
+		stmia	r0!, {r3 - r6}
+		subs	r2, r2, #16
+		bge	up_cpy_4_2_lp
+		adds	r2, r2, #12
+		blt	up_rest2
+
+up_cpy_x_2_lp:	mov	r3, r7, lsr #16
+		ldr	r7, [r1], #4
+		orr	r3, r3, r7, lsl #16
+		str	r3, [r0], #4
+		subs	r2, r2, #4
+		bge	up_cpy_x_2_lp
+
+up_rest2:	sub	r1, r1, #2
+		b	up_no_double_words
+
+up_cpy_4_3:	cmp	r2, #12
+		blt	up_cpy_x_3_lp
+		sub	r2, r2, #12
+
+up_cpy_4_3_lp:	mov	r3, r7, lsr #24
+		ldmia	r1!,{r4 - r7}
+		orr	r3, r3, r4, lsl #8
+		mov	r4, r4, lsr #24
+		orr	r4, r4, r5, lsl #8
+		mov	r5, r5, lsr #24
+		orr	r5, r5, r6, lsl #8
+		mov	r6, r6, lsr #24
+		orr	r6, r6, r7, lsl #8
+		stmia	r0!, {r3 - r6}
+		subs	r2, r2, #16
+		bge	up_cpy_4_3_lp
+		adds	r2, r2, #12
+		blt	up_rest3
+
+up_cpy_x_3_lp:	mov	r3, r7, lsr #24
+		ldr	r7, [r1], #4
+		orr	r3, r3, r7, lsl#8
+		str	r3, [r0], #4
+		subs	r2, r2, #4
+		bge	up_cpy_x_3_lp
+
+up_rest3:	sub	r1, r1, #1
+		b	up_no_double_words
+
+
+other_copy:	add	r1, r1, r2
+		add	r0, r0, r2
+		subs	r2, r2, #4
+		blt	down_no_double_words
+		ands	ip, r0, #3
+		bne	down_dest_not_aligned
+		ands	ip, r1, #3
+		bne	down_src_not_aligned
+
+down_rest:
+		subs	r2, r2, #8
+		blt	down_cpy_2_lp
+		subs	r2, r2, #0x14
+		blt	down_not_long_copy
+down_cpy_8_lp:
+		ldmdb	r1!, {r3 - r9, ip}
+		stmdb	r0!, {r3 - r9, ip}
+		subs	r2, r2, #32
+		bge	down_cpy_8_lp
+
+down_not_long_copy:
+		cmn	r2, #16
+		ldmgedb	r1!, {r3 - r6}
+		stmgedb	r0!, {r3 - r6}
+		subge	r2, r2, #16
+		adds	r2, r2, #20
+		ldmgedb	r1!, {r3 - r5}
+		stmgedb	r0!, {r3 - r5}
+		subge	r2, r2, #12
+down_cpy_2_lp:
+		adds	r2, r2, #8
+		blt	down_no_double_words
+		subs	r2, r2, #4
+		ldrlt	r3, [r1, #-4]!
+		strlt	r3, [r0, #-4]!
+		ldmgedb	r1!, {r3, r4}
+		stmgedb	r0!, {r3, r4}
+		subge	r2, r2, #4
+
+down_no_double_words:
+		adds	r2, r2, #4
+		EXITEQ
+		cmp	r2, #2
+		ldrb	r3, [r1, #-1]!
+		strb	r3, [r0, #-1]!
+		ldrgeb	r3, [r1, #-1]!
+		strgeb	r3, [r0, #-1]!
+		ldrgtb	r3, [r1, #-1]!
+		strgtb	r3, [r0, #-1]!
+		EXIT
+
+down_dest_not_aligned:
+		CMP	ip,#2
+		LDRB	r3,[r1,#-1]!
+		STRB	r3,[r0,#-1]!
+		LDRGEB	r3,[r1,#-1]!
+		STRGEB	r3,[r0,#-1]!
+		LDRGTB	r3,[r1,#-1]!
+		STRGTB	r3,[r0,#-1]!
+		SUBS	r2,r2,ip
+		BLT	down_no_double_words
+		ANDS	ip,r1,#3
+		BEQ	down_rest
+
+down_src_not_aligned:
+		BIC	r1,r1,#3
+		LDR	r3,[r1],#0
+		CMP	ip,#2
+		BLT	down_cpy_4_3
+		BEQ	down_cpy_4_2
+		CMP	r2,#12
+		BLT	down_cpy_x_1_lp
+		SUB	r2,r2,#12
+
+down_cpy_4_1_lp:
+		MOV	r7,r3,LSL#8
+		LDMDB	r1!,{r3,r4,r5,r6}
+		ORR	r7,r7,r6,LSR#24
+		MOV	r6,r6,LSL#8
+		ORR	r6,r6,r5,LSR#24
+		MOV	r5,r5,LSL#8
+		ORR	r5,r5,r4,LSR#24
+		MOV	r4,r4,LSL#8
+		ORR	r4,r4,r3,LSR#24
+		STMDB	r0!,{r4,r5,r6,r7}
+		SUBS	r2,r2,#16
+		BGE	down_cpy_4_1_lp
+		ADDS	r2,r2,#12
+		BLT	down_rest1
+
+down_cpy_x_1_lp:
+		MOV	ip,r3,LSL#8
+		LDR	r3,[r1,#-4]!
+		ORR	ip,ip,r3,LSR#24
+		STR	ip,[r0,#-4]!
+		SUBS	r2,r2,#4
+		BGE	down_cpy_x_1_lp
+
+down_rest1:
+		ADD	r1,r1,#3
+		B	down_no_double_words
+
+down_cpy_4_2:
+		CMP	r2,#12
+		BLT	down_cpy_x_2_lp
+		SUB	r2,r2,#12
+
+down_cpy_4_2_lp:
+		MOV	r7,r3,LSL#16
+		LDMDB	r1!,{r3,r4,r5,r6}
+		ORR	r7,r7,r6,LSR#16
+		MOV	r6,r6,LSL#16
+		ORR	r6,r6,r5,LSR#16
+		MOV	r5,r5,LSL#16
+		ORR	r5,r5,r4,LSR#16
+		MOV	r4,r4,LSL#16
+		ORR	r4,r4,r3,LSR#16
+		STMDB	r0!,{r4,r5,r6,r7}
+		SUBS	r2,r2,#16
+		BGE	down_cpy_4_2_lp
+		ADDS	r2,r2,#12
+		BLT	down_rest2
+
+down_cpy_x_2_lp:
+		MOV	ip,r3,LSL#16
+		LDR	r3,[r1,#-4]!
+		ORR	ip,ip,r3,LSR#16
+		STR	ip,[r0,#-4]!
+		SUBS	r2,r2,#4
+		BGE	down_cpy_x_2_lp
+
+down_rest2:
+		ADD	r1,r1,#2
+		B	down_no_double_words
+
+down_cpy_4_3:
+		CMP	r2,#12
+		BLT	down_cpy_x_3_lp
+		SUB	r2,r2,#12
+
+down_cpy_4_3_lp:
+		MOV	r7,r3,LSL#24
+		LDMDB	r1!,{r3,r4,r5,r6}
+		ORR	r7,r7,r6,LSR#8
+		MOV	r6,r6,LSL#24
+		ORR	r6,r6,r5,LSR#8
+		MOV	r5,r5,LSL#24
+		ORR	r5,r5,r4,LSR#8
+		MOV	r4,r4,LSL#24
+		ORR	r4,r4,r3,LSR#8
+		STMDB	r0!,{r4,r5,r6,r7}
+		SUBS	r2,r2,#16
+		BGE	down_cpy_4_3_lp
+		ADDS	r2,r2,#12
+		BLT	down_rest3
+down_cpy_x_3_lp:
+		MOV	ip,r3,LSL#24
+		LDR	r3,[r1,#-4]!
+		ORR	ip,ip,r3,LSR#8
+		STR	ip,[r0,#-4]!
+		SUBS	r2,r2,#4
+		BGE	down_cpy_x_3_lp
+down_rest3:
+		ADD	r1,r1,#1
+		B	down_no_double_words
+
diff -urNwbB linux/arch/arm/lib/string.S linux.arm/arch/arm/lib/string.S
--- linux/arch/arm/lib/string.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/string.S	Sun Mar  3 13:17:16 1996
@@ -0,0 +1,76 @@
+/*
+ * linux/arch/arm/lib/string.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+		.text
+# Prototype: char *strrchr(const char *s,char c);
+
+		.global	_strrchr
+_strrchr:	stmfd	sp!,{lr}
+		mov	r3,#0
+strrchrlp:	ldrb	r2,[r0],#1
+		teq	r2,r1
+		moveq	r3,r0
+		teq	r2,#0
+		bne	strrchrlp
+		mov	r0,r3
+		LOADREGS(fd, sp!, {pc})
+
+		.global	_memset
+_memset:	teq	r2, #0
+		RETINSTR(moveq,pc,lr)
+		stmfd	sp!, {lr}
+		mov	r3, r0
+		cmp	r2, #4
+		blt	memsetlp
+		ands	ip, r3, #3
+		beq	aligned
+		cmp	ip, #2
+		strltb	r1, [r3], #1
+		strleb	r1, [r3], #1
+		strb	r1, [r3], #1
+		rsb	ip, ip, #4
+		sub	r2, r2, ip
+aligned:	orr	ip, r1, r1, lsl #8
+		orr	ip, ip, ip, lsl #16
+		subs	r2, r2, #32
+		bmi	memsetl32
+memset32lp:	str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		LOADREGS(eqfd, sp!, {pc})
+		subs	r2, r2, #32
+		bpl	memset32lp
+memsetl32:	adds	r2, r2, #16
+		bmi	memsetl16
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		LOADREGS(eqfd, sp!, {pc})
+		sub	r2, r2, #16
+memsetl16:	adds	r2, r2, #8
+		bmi	memsetl8
+		str	ip, [r3], #4
+		str	ip, [r3], #4
+		LOADREGS(eqfd, sp!, {pc})
+		sub	r2, r2, #8
+memsetl8:	adds	r2, r2, #4
+		bmi	memsetl4
+		str	ip, [r3], #4
+		LOADREGS(eqfd, sp!, {pc})
+		sub	r2, r2, #4
+memsetl4:	add	r2, r2, #4
+memsetlp:	subs	r2, r2, #1
+		strgeb	r1, [r3], #1
+		bgt	memsetlp
+		LOADREGS(fd, sp!, {pc})
+
diff -urNwbB linux/arch/arm/lib/system.S linux.arm/arch/arm/lib/system.S
--- linux/arch/arm/lib/system.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/system.S	Sun Mar  3 13:17:43 1996
@@ -0,0 +1,232 @@
+/* 
+ * linux/arch/arm/lib/system.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ */
+
+#include <asm/assembler.h>
+#include "constants.h"
+
+		.text
+
+@ Prototype: move_to_user_mode()
+
+		.global	_move_to_user_mode
+_move_to_user_mode:
+		mov	r0, lr
+		tst	r0, #3
+		RETINSTR(moveq,pc,r0)
+		mov	r1, sp
+		sub	sp, sp,#2048
+		USERMODE
+		mov	r0, r0
+		mov	sp, r1
+		RETINSTR(mov,pc,r0)
+
+		.global	_abort
+_abort:		adr	r0, _abort_msg
+		b	_panic
+
+_abort_msg:	.ascii	"Abort received!\n\0"
+		.align
+
+LC0:
+		.word	_current
+LC1:
+		.word	_wantmap
+LC2:
+		.word	_page_nr
+LC3:
+		.word	_page_shift
+LC4:
+		.word	_currmap
+LC5:
+		.word	_swapper_pg_dir
+
+# Prototype: void switch_to(task_struct *next)
+
+		.global	_switch_to
+_switch_to:	ldr	r1, [pc, #LC0 - . - 8]		@ Get current task
+		ldr	r2, [r1]			@ Get struct
+		add	r2, r2, #TSS_UREGS+3*4		@ Get address of UREGS ***
+		stmia	r2!, {r13,r14}^			@ Save user registers
+		stmia	r2!, {r4-r14}			@ Save svc registers
+		mov	r4, r0
+		mov	r5, r1
+		mov	r0, r2
+		bl	_fpe_save
+		add	r0, r4, #TSS_UREGS+3*4
+		ldmia	r0!, {r13,r14}^			@ Get user registers
+		add	r0, r4, #TSS_FPESAVE & 0xf00	@ Get address of FPE save
+		add	r0, r0, #TSS_FPESAVE & 0x0ff	@ Get address of FPE save
+		bl	_fpe_restore
+		ldr	r1, [r4, #TSK_MM]
+		teq	r1, #0
+		ldrne	r0, [r1, #MM_PGDIR]
+		ldreq	r0, [pc, #LC5 - . - 8]
+		bl	_remap_task
+		add	r0, r4, #TSS_SREGS
+		SAVEIRQS
+		DISABLEIRQS
+		str	r4, [r5]
+#if defined (__arm2__) || defined (__arm3__)
+		LOADREGS(ia, r0, {r4 - r13, pc})	@ Get svc registers
+#else
+#error "Hmm.  This could be messy"
+		ldmia	r0!, {r4 - lr}
+		RESTOREIRQS
+		RETINSTR(mov,pc,lr)
+#endif
+
+#if defined (__arm2__) || defined (__arm3__)
+		.global	_remap_task
+_remap_task:	mov	ip, sp
+		stmfd	sp!, {r4 - r9, fp, ip, lr, pc}
+		sub	fp, ip, #4
+		ldr	r4, [pc, #LC1 - . - 8]
+		mov	r3, #0x00000F00
+		orr	r3, r3, #0x03F00000
+		ldr	r2, [pc, #LC2 - . - 8]
+		ldr	r2, [r2]
+removelp:	subs	r2, r2, #1
+		orrpl	r1, r3, r2
+		strpl	r1, [r4, r2, lsl#2]
+		subpls	r2, r2, #1
+		orrpl	r1, r3, r2
+		strpl	r1, [r4, r2, lsl#2]
+		subpls	r2, r2, #1
+		orrpl	r1, r3, r2
+		strpl	r1, [r4, r2, lsl#2]
+		subpls	r2, r2, #1
+		orrpl	r1, r3, r2
+		strpl	r1, [r4, r2, lsl#2]
+		bpl	removelp
+
+		adr	r6, phystomemc32	@ Convert to logical page number
+		ldr	r1, [pc, #LC3 - . - 8]
+		ldr	r7, [r1]
+		mov	r1, #32*1024*1024	@ Top of logical memory
+		mov	r1, r1, LSR r7		@ Convert to logical page number
+		sub	r8, r1, #1		@ Max. logical page no.
+
+invalidatelp:	ldr	r1, [r0, r8, lsl#2]	@ Get map info
+		tst	r1, #PAGE_PRESENT	@ Check to see if it is present
+		beq	nextpage		@ If not, try next
+		mov	r2, r1, lsr r7		@ convert entry to physical page number
+		and	r2, r2, #0xff		@ Ensure that we only have the page no.
+		ldr	r2, [r6, r2, lsl#2]	@ Convert to memc page number
+		tst	r1, #PAGE_ACCESSED	@ check accessed
+		tstne	r1, #PAGE_USER
+		beq	not_accessed
+		bic	r2, r2, #0x200
+		tst	r1, #PAGE_RW
+		tstne	r1,#PAGE_DIRTY
+		bicne	r2, r2, #0x300		@ Read/Write
+
+not_accessed:	mov	r1, r8, lsl r7		@ Shift logical page into correct position
+		mov	r3, r1, lsl #9		@ get low bits of page number
+		orr	r2, r2, r3, lsr #9
+		and	r1, r1, #0x01800000
+		orr	r2, r2, r1, lsr #(23 - 10)
+		and	r1, r2, #255
+		str	r2, [r4, r1, lsl #2]	@ save in 'wanted' map table
+nextpage:	subs	r8,r8,#1
+		bpl	invalidatelp
+
+@ update the map to 'wantmap' - wantmap is in r11, r10 = page_nr
+@ (interrupts can be enabled, and will be soon...)
+map_update:	ldr	r3, [pc, #LC2 - . - 8]
+		ldr	r3, [r3]
+		sub	r3, r3, #1
+		ldr	r5, [pc, #LC4 - . - 8]
+map_updatelp:	ldr	r1, [r4,r3,LSL#2]
+		ldr	r2, [r5,r3,LSL#2]
+		teq	r2, r1
+		strne	r1, [r5,r3,LSL#2]
+		strneb	r1,[r1]
+		subs	r3,r3,#1
+		bpl	map_updatelp
+		ldmea	fp, {r4 - r9, fp, sp, pc}^
+
+		.align
+
+#define PCD(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \
+		.word	a0 | 0x03800300 ;\
+		.word	a1 | 0x03800300 ;\
+		.word	a2 | 0x03800300 ;\
+		.word	a3 | 0x03800300 ;\
+		.word	a4 | 0x03800300 ;\
+		.word	a5 | 0x03800300 ;\
+		.word	a6 | 0x03800300 ;\
+		.word	a7 | 0x03800300 ;\
+		.word	a8 | 0x03800300 ;\
+		.word	a9 | 0x03800300 ;\
+		.word	aa | 0x03800300 ;\
+		.word	ab | 0x03800300 ;\
+		.word	ac | 0x03800300 ;\
+		.word	ad | 0x03800300 ;\
+		.word	ae | 0x03800300 ;\
+		.word	af | 0x03800300
+
+@ Table to map from page number to vidc page number
+phystomemc32:
+		
+		PCD(	0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78)
+		PCD(	0x01,0x09,0x11,0x19,0x21,0x29,0x31,0x39,0x41,0x49,0x51,0x59,0x61,0x69,0x71,0x79)
+		PCD(	0x04,0x0C,0x14,0x1C,0x24,0x2C,0x34,0x3C,0x44,0x4C,0x54,0x5C,0x64,0x6C,0x74,0x7C)
+		PCD(	0x05,0x0D,0x15,0x1D,0x25,0x2D,0x35,0x3D,0x45,0x4D,0x55,0x5D,0x65,0x6D,0x75,0x7D)
+		PCD(	0x02,0x0A,0x12,0x1A,0x22,0x2A,0x32,0x3A,0x42,0x4A,0x52,0x5A,0x62,0x6A,0x72,0x7A)
+		PCD(	0x03,0x0B,0x13,0x1B,0x23,0x2B,0x33,0x3B,0x43,0x4B,0x53,0x5B,0x63,0x6B,0x73,0x7B)
+		PCD(	0x06,0x0E,0x16,0x1E,0x26,0x2E,0x36,0x3E,0x46,0x4E,0x56,0x5E,0x66,0x6E,0x76,0x7E)
+		PCD(	0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,0x3F,0x47,0x4F,0x57,0x5F,0x67,0x6F,0x77,0x7F)
+		PCD(	0x80,0x88,0x90,0x98,0xA0,0xA8,0xB0,0xB8,0xC0,0xC8,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8)
+		PCD(	0x81,0x89,0x91,0x99,0xA1,0xA9,0xB1,0xB9,0xC1,0xC9,0xD1,0xD9,0xE1,0xE9,0xF1,0xF9)
+		PCD(	0x84,0x8C,0x94,0x9C,0xA4,0xAC,0xB4,0xBC,0xC4,0xCC,0xD4,0xDC,0xE4,0xEC,0xF4,0xFC)
+		PCD(	0x85,0x8D,0x95,0x9D,0xA5,0xAD,0xB5,0xBD,0xC5,0xCD,0xD5,0xDD,0xE5,0xED,0xF5,0xFD)
+		PCD(	0x82,0x8A,0x92,0x9A,0xA2,0xAA,0xB2,0xBA,0xC2,0xCA,0xD2,0xDA,0xE2,0xEA,0xF2,0xFA)
+		PCD(	0x83,0x8B,0x93,0x9B,0xA3,0xAB,0xB3,0xBB,0xC3,0xCB,0xD3,0xDB,0xE3,0xEB,0xF3,0xFB)
+		PCD(	0x86,0x8E,0x96,0x9E,0xA6,0xAE,0xB6,0xBE,0xC6,0xCE,0xD6,0xDE,0xE6,0xEE,0xF6,0xFE)
+		PCD(	0x87,0x8F,0x97,0x9F,0xA7,0xAF,0xB7,0xBF,0xC7,0xCF,0xD7,0xDF,0xE7,0xEF,0xF7,0xFF)
+
+		.data
+		.global _currmap
+# The current memc mapping
+_currmap:
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+# The wanted memc mapping
+_wantmap:
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+		.word	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+
+#else
+#error "You'll have to write this part yourself"
+#endif
diff -urNwbB linux/arch/arm/lib/traps.S linux.arm/arch/arm/lib/traps.S
--- linux/arch/arm/lib/traps.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/traps.S	Sun Mar  3 13:18:51 1996
@@ -0,0 +1,741 @@
+/*
+ * linux/arch/arm/lib/traps.S
+ *
+ * Copyright (C) 1995, 1996 Russell King.
+ *
+ * Low-level vector interface routines
+ *
+ * Design issues:
+ *  - We have several modes that each vector can be called from,
+ *    each with its own set of registers.  On entry to any vector,
+ *    we *must* save the registers used in *that* mode.
+ *
+ *  - This code must be as fast as possible.
+ *
+ * There are a few restrictions on the vectors:
+ *  - the SWI vector cannot be called from *any* non-user mode
+ *
+ *  - the FP emulator is *never* called from *any* non-user mode undefined instruction.
+ *
+ * Ok, so this file may be a mess, but its as efficient as possible while
+ * adhering to the above criteria.
+ */
+
+#include <asm/assembler.h>
+#include <asm/errno.h>
+		.text
+
+@ Offsets into task structure
+@ ---------------------------
+@
+#define STATE		0
+#define COUNTER		4
+#define PRIORITY	8
+#define SIGNAL		12
+#define BLOCKED		16
+#define FLAGS		20
+#define ERRNO		24
+
+#define PF_TRACESYS	0x20
+
+@ Bad Abort numbers
+@ -----------------
+@
+#define BAD_PREFETCH	0
+#define BAD_DATA	1
+#define BAD_ADDREXCPTN	2
+#define BAD_IRQ		3
+
+@ Address of start of IOC
+@
+#define IOC		0x03200000
+
+@ Number of syscalls accepted
+@
+#define NSYS_CALL	142
+
+@ OS version number used in SWIs
+@  RISC OS is 0
+@  RISC iX is 8
+@
+#define OS_NUMBER	9
+
+@
+@ Stack format (ensured by USER_* and SVC_*)
+@
+#define S_OLD_R0	64
+#define S_PC		60
+#define S_LR		56
+#define S_SP		52
+#define S_IP		48
+#define S_FP		44
+#define S_R10		40
+#define S_R9		36
+#define S_R8		32
+#define S_R7		28
+#define S_R6		24
+#define S_R5		20
+#define S_R4		16
+#define S_R3		12
+#define S_R2		8
+#define S_R1		4
+#define S_R0		0
+
+#include "constants.h"
+
+#define USER_SAVE_ALL				\
+		str	r0, [sp, #-4]!		;\
+		str	lr, [sp, #-4]!		;\
+		sub	sp, sp, #15*4		;\
+		stmia	sp, {r0 - lr}^		;\
+		mov	r0, r0			;\
+		mov	fp, #0
+
+#define SVC_SAVE_ALL				\
+		str	sp, [sp, #-16]!		;\
+		str	lr, [sp, #8]		;\
+		str	lr, [sp, #4]		;\
+		stmfd	sp!, {r0 - r12}		;\
+		mov	r0, #-1			;\
+		str	r0, [sp, #S_OLD_R0]	;\
+		mov	fp, #0
+
+#define SVC_IRQ_SAVE_ALL			\
+		str	sp, [sp, #-16]!		;\
+		str	lr, [sp, #4]		;\
+		ldr	lr, [pc, #LC4 - . - 8]	;\
+		ldr	lr, [lr]		;\
+		str	lr, [sp, #8]		;\
+		stmfd	sp!, {r0 - r12}		;\
+		mov	r0, #-1			;\
+		str	r0, [sp, #S_OLD_R0]	;\
+		mov	fp, #0
+
+#define USER_RESTORE_ALL			\
+		ldmia	sp, {r0 - lr}^		;\
+		mov	r0, r0			;\
+		add	sp, sp, #15*4		;\
+		ldr	lr, [sp], #8		;\
+		bics	pc, lr, #0x0C000003	
+
+#define SVC_RESTORE_ALL				\
+		ldmfd	sp, {r0 - pc}^
+		
+		.global	_ret_from_sys_call
+
+@ ===============================================================================
+@ SWI handler
+@ -------------------------------------------------------------------------------
+@
+@ We now handle sys-call tracing, and the errno in the task structure.
+@ Still have a problem with >4 arguments for functions.  Theres only
+@ a couple of functions in the code that have 5 arguments, so Im not
+@ too worried.
+@
+
+LC1:
+		.word	_current
+LC2:
+		.word	_sys_call_table
+
+vector_swi:	USER_SAVE_ALL
+		bic	lr, lr, #0xfc000003		@ get pc value from link register
+		ldr	r4, [lr, #-4]			@ get swi instruction
+		teqp	pc, #0x00000003			@ enable irqs
+		bic	r4, r4, #0xff000000		@ mask off swi op code
+		eor	r4, r4, #OS_NUMBER << 20	@ check OS number
+		tst	r4, #0x00f00000
+		bne	bad
+retry:		cmp	r4, #NSYS_CALL			@ check upper syscall limit
+		bcs	swi_bad_call
+		bl	_arm3_flushcache
+
+		ldr	r5, [pc, #LC1 - . - 8]
+		ldr	r5, [r5]
+		mov	ip, #0				@ zero errno
+		str	ip, [r5, #ERRNO]
+
+		ldr	ip, [r5, #FLAGS]		@ check for syscall tracing
+		tst	ip, #PF_TRACESYS
+		bne	trace_this_syscall
+
+		ldr	ip, [pc, #LC2 - . - 8]
+		mov	r9, sp				@ hack for routines needing > 4 values
+		mov	lr, pc
+		ldr	pc, [ip, r4, lsl #2]		@ call sys routine
+
+		ldr	ip, [r5, #ERRNO]		@ check errno
+		rsbs	ip, ip, #0
+		movne	r0, ip
+		streq	r0, [sp, #0]			@ returned r0
+		bl	_arm3_flushcache
+		b	_ret_from_sys_call
+
+trace_this_syscall:
+		ldr	r7, [sp, #S_IP]
+		mov	r0, #0
+		str	r0, [sp, #S_IP]
+		bl	_syscall_trace			@ trace entry [IP must = 0]
+		str	r7, [sp, #S_IP]
+		ldmia	sp, {r0 - r3}			@ have to reload r0 - r3
+
+		ldr	ip, [pc, #LC2 - . - 8]
+		mov	r9, sp				@ hack for routines needing > 4 values
+		mov	lr, pc
+		ldr	pc, [ip, r4, lsl #2]		@ call sys routine
+
+		ldr	ip, [r5, #ERRNO]
+		rsbs	ip, ip, #0
+		movne	r0, ip
+		str	r0, [sp, #0]			@ returned r0
+
+		mov	r0, #1
+		str	r0, [sp, #S_IP]
+		bl	_syscall_trace			@ trace exit [IP must != 0]
+		str	r7, [sp, #S_IP]
+		bl	_arm3_flushcache
+		b	_ret_from_sys_call
+
+
+swi_bad_call:	cmp	r4, #(KSWI_SYS_BASE - KSWI_BASE)@ check for arm private syscalls
+		bcs	arm_sys_call
+		bl	_sys_ni_syscall
+		str	r0, [sp, #0]			@ returned r0
+		b	_ret_from_sys_call
+
+bad:		bl	_deferred
+		ldmfd	sp, {r0 - r3}
+		b	retry
+
+arm_sys_call:	bic	r0, r4, #0x000f0000
+		mov	r1, sp
+		bl	_arm_syscall
+		b	_ret_from_sys_call
+
+@ ===============================================================================
+@ Undefined instruction handler
+@ -------------------------------------------------------------------------------
+@
+@ TODO:
+@   To allow the kernel to work on ARM2, the undefined instruction handler
+@   will have to cope with the swp instructions.
+@
+
+LC9:
+		.word	_fp_enter
+		.word	_fp_save
+		.word	_fp_restore
+
+		.global	_fpreturn
+		.global	_fpundefinstr
+vector_undefinstr:
+		tst	lr,#3
+		bne	undef_not_user
+		USER_SAVE_ALL			@ USER mode undefined instructions
+		DISABLEIRQS			@ MUST disable interrupts.
+
+_undefinstr:	ldr	r3, [pc, #LC9 - . - 8]
+		ldr	pc, [r3]		@ Call FP module (when loaded)
+
+_fpundefinstr:	mov	r1,sp			@ Called by FP module on undefined instr
+		ENABLEIRQS
+		mov	r2,#0
+		bl	_do_undefinstr
+_fpreturn:	b	_ret_from_sys_call	@ Normal FP exit
+
+undef_not_user:	SVC_SAVE_ALL			@ Non-user mode
+		bic	r0, lr, #0xfc000003
+		and	r2, lr, #3
+		sub	r0, r0, #4
+		mov	r1, sp
+		bl	_do_undefinstr
+		SVC_RESTORE_ALL
+
+		.global	_fpe_save
+_fpe_save:	ldr	r1, [pc, #LC9 - . - 4]
+		ldr	pc, [r1]
+
+		.global	_fpe_restore
+_fpe_restore:	ldr	r1, [pc, #LC9 - . - 0]
+		ldr	pc, [r1]
+
+_fpnull:	mov	pc, lr
+
+@ ===============================================================================
+@ Prefetch abort handler
+@ -------------------------------------------------------------------------------
+
+vector_prefetch:
+		sub	lr, lr, #4
+		tst	lr, #3
+		bne	prefetch_not_user
+		USER_SAVE_ALL
+		tst	lr, #0x08000000		@ Only enable irqs if enabled before call
+		teqeqp	pc, #0x00000003		@ NOT a problem - doesnt change mode
+		bic	r0, lr, #0xfc000003	@ Min address of abort
+		add	r1, r0, #4		@ Max address of abort
+		mov	r2, #0x100		@ Error code
+		mov	r3, sp			@ Tasks registers
+		bl	_do_dataabort
+		teq	r0, #0			@ If zero, we believe this abort..
+		bne	_ret_from_sys_call
+#ifdef DEBUG_UNDEF
+		adr	r0,t			@ Otherwise it was probably an undefined
+		bl	_printk			@ instruction.  (I do wish that I had a
+#endif
+		ldr	lr, [sp,#S_PC]		@ program to test this on.  I think its
+		b	_undefinstr		@ broken at the moment though!)
+
+prefetch_not_user:
+		SVC_SAVE_ALL			@ Prefetch aborts are definitely *not*
+		mov	r0, sp			@ allowed in non-user modes.  We cant
+		mov	r1, #BAD_PREFETCH	@ recover from this problem.
+		and	r2, lr, #3
+		b	_bad_mode		@ Doesnt return
+
+#ifdef DEBUG_UNDEF
+t:	.ascii "*** undef ***\r\n\0"
+	.align
+#endif
+
+@ ===============================================================================
+@ Address exception handler
+@ -------------------------------------------------------------------------------
+@
+@ These aren't too critical. (they're not supposed to happen).
+@
+
+vector_addrexcptn:
+		sub	lr, lr, #8
+		tst	lr, #3
+		bne	addrexcptn_not_user
+		USER_SAVE_ALL
+		teq	pc, #0x00000003
+		bic	r0, lr, #0xfc000003	@ Point to instruction
+		mov	r1, sp			@ Point to registers
+		mov	r2, #0x400
+		bl	_do_excpt
+		b	_ret_from_sys_call
+
+addrexcptn_not_user:
+		SVC_SAVE_ALL
+		and	r2, lr, #3
+		teq	r2, #3
+		bne	addrexcptn_illegal_mode
+		teqp	pc, #0x00000003		@ NOT a problem - doesnt change mode
+		bic	r0, lr, #0xfc000003
+		mov	r1, sp
+		orr	r2, r2, #0x400
+		bl	_do_excpt
+		ldmia	sp, {r0 - lr}		@ I cant remember the reason I changed this...
+		add	sp, sp, #15*4
+		movs	pc, lr
+
+addrexcptn_illegal_mode:
+		mov	r0, sp
+		str	lr, [sp, #-4]!
+		orr	r1, r2, #0x0c000000
+		teqp	r1, #0			@ change into mode (wont be user mode)
+		mov	r0, r0
+		mov	r1, r8			@ Any register from r8 - r14 can be banked
+		mov	r2, r9
+		mov	r3, r10
+		mov	r4, r11
+		mov	r5, r12
+		mov	r6, r13
+		mov	r7, r14
+		teqp	pc, #0x04000003		@ back to svc
+		mov	r0, r0
+		stmfd	sp!, {r1-r7}
+		ldmia	r0, {r0-r7}
+		stmfd	sp!, {r0-r7}
+		mov	r0, sp
+		mov	r1, #BAD_ADDREXCPTN
+		b	_bad_mode
+
+@ ===============================================================================
+@ Trap initialisation
+@ -------------------------------------------------------------------------------
+@
+@ Note - FIQ code has changed.  The default is a couple of words in 0x1c, 0x20
+@ that call _unexp_fiq.  Nowever, we now copy the FIQ routine to 0x1c (removes
+@ some excess cycles).
+@
+
+swi_reset:	swi	SYS_ERROR0
+		ldr	pc, . + 4
+		.word	_unexp_fiq
+
+fiqmsg:		.ascii	"*** Unexpeced FIQ\n\0"
+
+_unexp_fiq:	mov	r12, #IOC
+		strb	r12, [r12, #0x38]	@ Disable FIQ register
+		teqp	pc, #0x0c000003
+		mov	r0, r0
+		stmfd	sp!, {r0 - r3, ip, lr}
+		adr	r0, fiqmsg
+		bl	_printk
+		ldmfd	sp!, {r0 - r3, ip, lr}
+		teqp	pc, #0x0c000001
+		mov	r0, r0
+		movs	pc, lr
+
+		.global	_trap_init
+_trap_init:	stmfd	sp !,{r4 - r9,lr}	@ Save link register
+		teqp	pc, #0x0c000003
+		mov	r0, #IOC
+		str	r0, [r0, #0x18]
+		str	r0, [r0, #0x28]
+		str	r0, [r0, #0x38]
+		mov	r0, #0			@ Lowest location
+		adr	r1, swi_reset
+		adr	r2, vector_undefinstr - 12
+		adr	r3, vector_swi - 16
+		adr	r4, vector_prefetch - 20
+		adr	r5, vector_data - 24
+		adr	r6, vector_addrexcptn - 28
+		adr	r7, vector_IRQ - 32
+		mov	ip, #0xea000000		@ B instruction
+		orr	r2, ip, r2, lsr #2
+		orr	r3, ip, r3, lsr #2
+		orr	r4, ip, r4, lsr #2
+		orr	r5, ip, r5, lsr #2
+		orr	r6, ip, r6, lsr #2
+		orr	r7, ip, r7, lsr #2
+		ldmia	r1, {r1, r8, r9}
+		stmia	r0!, {r1 - r9}		@ Save all into page 0 ram
+		ldmfd	sp!, {r4 - r9, pc}^
+
+LC4:
+		.word	_irq_temp
+LC5:
+		.word	_irqjump
+
+@ ===============================================================================
+@ Interrupt (IRQ) handler (r13 points to irq temp save area)
+@ MOD: if in user mode, then *no* kernel routine is running, so dont have to
+@      save svc lr
+@ -------------------------------------------------------------------------------
+vector_IRQ:	ldr	r13, [pc, #LC4 - . - 8]	@ Ill leave this one in just in case...
+		sub	lr, lr, #4
+		str	lr, [r13]
+		tst	lr, #3
+		bne	IRQ_not_user
+		teqp	pc, #0x08000003
+		mov	r0, r0
+		ldr	lr, [pc, #LC4 - . - 8]
+		ldr	lr, [lr]
+		USER_SAVE_ALL
+urepeat:	mov	r4, #IOC			@ point at IOC
+		ldrb	r1, [r4, #0x24]		@ get high priority first
+		adr	r2, irq_prio_h
+		teq	r1, #0
+		ldreqb	r1, [r4, #0x14]		@ get low priority
+		adreq	r2, irq_prio_l
+		teq	r1, #0
+		beq	_ret_from_sys_call
+		ldrb	r0, [r2, r1]		@ Get IRQ number
+		ldr	r2, [pc, #LC5 - . - 8]
+		mov	r1, sp
+		mov	lr, pc
+		@
+		@ routine gets called with r0 = interrupt number, r1 = struct pt_regs *
+		@
+		ldr	pc, [r2, r0, lsl#2]
+		mov	r2, #0
+		teq	r0, #0			@ Check to see if it is a fast IRQ
+		beq	_ret_from_sys_call
+		USER_RESTORE_ALL
+
+LC65:
+		.word	_intr_count		@ -8
+		.word	_bh_mask		@ -4
+		.word	_bh_active		@ -0
+
+IRQ_not_user:	teqp	pc, #0x08000003
+		mov	r0, r0
+		SVC_IRQ_SAVE_ALL
+                and	r2, lr, #3
+		teq	r2, #3
+		bne	irq_illegal_mode
+srepeat:	mov	r4, #IOC		@ point at IOC
+		ldrb	r1, [r4, #0x24]		@ get high priority first
+		adr	r2, irq_prio_h
+		teq	r1, #0
+		ldreqb	r1, [r4, #0x14]		@ get low priority
+		adreq	r2, irq_prio_l
+		teq	r1, #0
+		beq	no_irq2
+		ldrb	r0, [r2, r1]		@ Get IRQ number
+		ldr	r2, [pc, #LC5 - . - 8]
+		mov	r1, sp
+		mov	lr, pc
+		@
+		@ routine gets called with r0 = interrupt number, r1 = struct pt_regs *
+		@
+		ldr	pc, [r2, r0, lsl#2]
+		mov	r2, #1
+		teq	r0, #0			@ Check to see if it is a fast IRQ
+		bne	srepeat
+		ldr	r0, [pc, #LC65 - . - 8]
+		ldr	r1, [r0]
+		teq	r1, #0
+		bne	srepeat
+		mov	r4, r0
+		mov	r5, r1
+		ldr	r6, [pc, #LC65 - . - 4]
+		ldr	r7, [pc, #LC65 - . - 0]
+recheck_bh2:	ldr	r0, [r6]
+		ldr	r1, [r7]
+		tst	r0, r1
+		beq	srepeat
+		add	r0, r5, #1
+		str	r0, [r4]
+		mov	r8, pc
+		teqp	pc, #0x00000003
+		bl	_do_bottom_half
+		teqp	r8, #0
+		str	r5, [r4]
+		b	recheck_bh2
+no_irq2:	SVC_RESTORE_ALL
+
+irq_illegal_mode:
+		mov	r0, sp
+		mov	r1, #BAD_IRQ
+		b	_bad_mode
+
+@ ===============================================================================
+@ Data abort handler code
+@ -------------------------------------------------------------------------------
+
+vector_data:	sub	lr, lr, #8		@ Correct lr
+		tst	lr, #3
+		bne	data_not_user
+		USER_SAVE_ALL
+		tst	lr, #0x08000000
+		teqeqp	pc, #0x00000003		@ NOT a problem - doesnt change mode
+		bic	r0, lr, #0xFC000003
+		mov	r2, #0
+		mov	r3, sp
+		bl	data_do
+		b	_ret_from_sys_call
+
+data_not_user:	
+		SVC_SAVE_ALL
+		and	r2,lr,#3
+		teq	r2,#3
+		bne	data_illegal_mode
+		tst	lr,#0x08000000
+		teqeqp	pc,#0x00000003		@ NOT a problem - doesnt change mode
+		bic	r0,lr,#0xFC000003
+		mov	r3,sp
+		bl	data_do
+		SVC_RESTORE_ALL
+
+data_illegal_mode:
+		mov	r0, sp
+		mov	r1, #BAD_DATA
+		b	_bad_mode
+
+@ Interrupt table (incorporates priority)
+
+irq_prio_h:	.byte	 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	12, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+		.byte	13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+
+irq_prio_l:	.byte	 0, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 4, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+		.byte	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+
+@ ----------------------------------------------------------------------------------------------
+@
+@ This is where I wish that the ARM would tell you which address aborted.
+@
+
+data_do:	ldr	r5, [r0]		@ Get instruction
+		tst	r5, #1<<20		@ Check to see if it is a write instruction
+		orreq	r2, r2, #0x20		@ Indicate write instruction
+		tst	r5, #1 << 26		@ Check LDMSTM/LDRSTRLDCSTC
+		bne	data_ldrstrldcstc	@ If set, then it is not LDMSTM
+
+		mov	r6, #1 << 15
+		mov	r7, #0
+data_ldmstm_cr:	tst	r5, r6			@ Check this register
+		addne	r7, r7, #4		@ If set, then another register
+		movs	r6, r6, lsr#1
+		bne	data_ldmstm_cr
+		eor	r1, r5, r5, lsl#2
+		tst	r1, #1<<23		@ Check inc/dec bit ^ writeback
+		rsbeq	r7, r7, #0
+		mov	r4, r5, lsr#(16-2)	@ Get base register
+		and	r4, r4, #15*4		@ Mask out register
+		ldr	r0, [r3, r4]		@ Get reg
+		add	r7, r0, r7		@ Do correction (signed)
+		moveq	r1, r0
+		moveq	r0, r7
+		movne	r1, r7
+		tst	r5, #1<<21
+		strne	r7, [r3, r4]
+		teq	r4, #15*4		@ CHECK FOR PC
+		biceq	r1, r1, #0xfc000003
+		biceq	r0, r0, #0xfc000003
+		orr	r2, r2, #0x200
+		b	_do_dataabort
+
+data_ldrstrldcstc:
+		mov	r6, r5, lsr#(16-2)	@ Get Rn
+		and	r6, r6, #15*4		@ Mask out register
+		teq	r6, #15*4		@ CHECK FOR PC
+		ldr	r6, [r3, r6]		@ Get register
+		biceq	r6, r6, #0xfc000003
+		tst	r5, #1 << 24		@ Check index type bit
+		beq	ldrstrpost		@ Post indexing?
+		tst	r5, #1 << 27		@ Get LDRSTR/LDCSTC bit
+		bne	instrldcstc		@ If set, do LDC/STC instruction
+		tst	r5, #1 << 25		@ Check immediate bit
+		bne	ldrstrregval		@ Register is specified
+		mov	r1, #0xff
+		orr	r1, r1, #0xf00
+		and	r7, r5, r1
+ldrstrsubadd:
+		tst	r5, #1 << 23
+		addne	r1, r6, r7
+		movne	r0, r6
+		moveq	r1, r6			@ Set higher address same
+		subeq	r0, r6, r7		@ Apply correction
+		orr	r2, r2, #0x300
+		b	_do_dataabort
+
+ldrstrpost:	mov	r0, r6
+		mov	r1, r0			@ Set higher address same
+		tst	r5, #1 << 21
+		bicne	r2, r2, #3		@ If T bit is set, force user mode
+		orr	r2, r2, #0x400
+		b	_do_dataabort
+
+ldrstrregval:	and	r7, r5,#15
+		teq	r7, #15			@ CHECK FOR PC
+		ldr	r7, [r3, r7, lsl#2]	@ Get Rm
+		biceq	r7, r7, #0xfc000003
+		and	r8, r5, #0x60		@ Get shift types
+		mov	r9, r5, lsr#7
+		and	r9, r9, #31		@ Get shift amount
+		teq	r8, #0			@ LSL shift
+		moveq	r7, r7, lsl r9
+		teq	r8, #0x20		@ LSR shift
+		moveq	r7, r7, lsr r9
+		teq	r8, #0x40		@ ASR shift
+		moveq	r7, r7, asr r9
+		teq	r8, #0x60		@ ROR shift
+		moveq	r7, r7, ror r9
+		tst	r5, #1 << 23
+		addne	r0, r6, r7
+		subeq	r0, r6, r7		@ Apply correction
+		mov	r1, r0
+		orr	r2, r2, #0x500
+		b	_do_dataabort
+
+instrldcstc:	and	r7, r5, #255		@ Get offset
+		orr	r2, r2, #0x600
+		B	ldrstrsubadd
+
+@ ===============================================================================
+@
+@ All exits to user mode from the kernel go through this code.
+@
+
+LC6:
+		.word	_intr_count		@ -8
+		.word	_bh_mask		@ -4
+		.word	_bh_active		@ -0
+		.word	_need_resched		@ +4
+		.word	_current		@ +8
+		.word	_init_task		@ +12
+
+reschedule:	bl	_schedule
+
+_ret_from_sys_call:
+		ldr	r4, [pc, #LC6 - . - 8]
+		ldr	r5, [r4]
+		teq	r5, #0
+		bne	ret_no_check
+		ldr	r6, [pc, #LC6 - . - 4]
+		ldr	r7, [pc, #LC6 - . - 0]
+recheck_bh:	ldr	r0, [r6]
+		ldr	r1, [r7]
+		tst	r0, r1
+		beq	no_handle_bh
+handle_bottom_half:
+		add	r0, r5, #1
+		str	r0, [r4]
+		mov	r8, pc
+		teqp	pc, #0x00000003
+		bl	_do_bottom_half
+		teqp	r8, #0
+		str	r5, [r4]
+		b	recheck_bh
+no_handle_bh:	teqp	pc, #0x00000003
+		ldr	r0, [pc, #LC6 - . + 4]
+		ldr	r0, [r0]
+		teq	r0, #0
+		bne	reschedule
+		ldr	r4, [pc, #LC6 - . + 8]
+		ldr	r4, [r4]
+		ldr	r0, [pc, #LC6 - . + 12]
+		teq	r4, r0
+		beq	ret_no_check
+		ldr	r0, [r4, #STATE]
+		teq	r0, #0
+		bne	reschedule
+		ldr	r0, [r4, #COUNTER]
+		teq	r0, #0
+		beq	reschedule
+		ldr	r1, [r4, #SIGNAL]
+		ldr	r0, [r4, #BLOCKED]
+		bics	r1, r1, r0
+		movne	r1, sp
+		blne	_do_signal
+ret_no_check:	USER_RESTORE_ALL
+
+		.global	_arm3_flushcache
+_arm3_flushcache:
+		mcr	15, 0, r0, c1, c0	@ flush cache
+		movs	pc, lr
+
+		.data
+
+		.global	_fp_enter
+		.global	_fp_save
+		.global	_fp_restore
+_fp_enter:	.word	_fpundefinstr
+_fp_save:	.word	_fpnull
+_fp_restore:	.word	_fpnull
+
+_irq_temp:	.word	0
+
diff -urNwbB linux/arch/arm/mm/Makefile linux.arm/arch/arm/mm/Makefile
--- linux/arch/arm/mm/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/Makefile	Sun Feb 11 09:32:43 1996
@@ -0,0 +1,13 @@
+#
+# Makefile for the linux arm-specific parts of the memory manager.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := mm.o
+O_OBJS   := init.o fault.o
+
+include $(TOPDIR)/Rules.make
diff -urNwbB linux/arch/arm/mm/fault.c linux.arm/arch/arm/mm/fault.c
--- linux/arch/arm/mm/fault.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/fault.c	Sun Mar  3 13:08:26 1996
@@ -0,0 +1,256 @@
+/*
+ *  linux/arch/arm/mm/fault.c
+ *
+ *  Copyright (C) 1995  Linus Torvalds
+ *  Modifications for ARM processor (c) 1995, 1996 Russell King
+ */
+
+#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 <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+
+extern void die_if_kernel(char *, struct pt_regs *, long, int, int);
+
+#ifdef DEBUG 
+#define IDEBUG(x) x
+#else
+#define IDEBUG(x)
+#endif
+
+#ifdef DEBUG
+static int last_min_addr=0,warned=0,last_pid=0;
+
+static void debug(unsigned long min_addr, unsigned long max_addr, int mode, unsigned long *regs)
+{
+	if ((mode & 0x600) == 0x600)
+		printk("LDC/STC abort: %08lx-%08lx (sp=%08lx, lr=%08lx, pc=%08lx)\n",
+			min_addr, max_addr, regs[13], regs[14], regs[15]);
+
+	if((regs[15] & 0x03FFFFFC) > LOGICAL_END)
+	{
+		unsigned long *sp;
+		int n,i;
+		printk("Illegal PC: %08lx-%08lx (sp=%08lx, lr=%08lx, pc=%08lx)!\nStack:\n",
+			min_addr, max_addr, regs[13], regs[14], regs[15]);
+
+		n = (regs[13]>>2) & 3;
+		sp = (unsigned long *)(regs[13] & 0xFFFFFFF0);
+		printk("%08lx: ", (unsigned long) sp);
+		for(i=0; i<n; i++)
+			printk("%8s  ","");
+
+		for(;sp<(unsigned long *)0x01800000; sp+=4)
+		{
+			if(n==0)
+				printk("\n%08lx: ", (unsigned long) sp);
+			for(;n<4; n++)
+				printk("%08lx  ", (unsigned long) sp[n]);
+			n=0;
+		}
+		printk("\n");
+		die_if_kernel("Opps", (struct pt_regs *)regs, error_code, mode, SIGBUS);
+	}
+
+	if(last_min_addr == min_addr && last_pid == current->pid)
+	{
+		warned++;
+		if (warned==10)
+		{
+			printk("Repetitive address abort %08lx-%08lx err %08lx (sp=%08lx, lr=%08lx, pc=%08lx)\n",
+				min_addr, max_addr, error_code, regs[13], regs[14], regs[15]);
+
+			printk("Page entry: %08lx\n", pte_val(*pte));
+
+			if(regs[13]>LOGICAL_END)
+			{
+				int i, j;
+				printk("Stack: ");
+				for(i = 16, j=0; i<(16 + 200); i++, j++)
+				{
+					if (j && !(j & 7))
+						printk("\n");
+					printk("%08lx ",((unsigned long *)regs[13])[i]);
+				}
+				printk("\n");
+			}
+			send_sig(SIGSEGV, current, 1);
+			while(1);
+		}
+	}
+
+	if(last_min_addr != min_addr || last_pid != current->pid)
+		warned=0;
+	last_min_addr = min_addr;
+	last_pid = current->pid;
+}
+#endif
+
+/*
+ * This routine handles page faults.  It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ *
+ * error_code:
+ *	bit 0 == 0 means no page found, 1 means protection fault
+ *	bit 1 == 0 means read, 1 means write
+ *	bit 2 == 0 means kernel, 1 means user-mode
+ */
+void do_page_fault1(unsigned long address,unsigned long error_code,struct pt_regs *regs)
+{
+	struct vm_area_struct * vma;
+	int why=0;
+
+/* Handle 'page_accessed' and 'page_dirty' bits... */
+
+	/* get the address */
+	for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
+		if (!vma)
+			goto bad_area;
+		if (vma->vm_end > address)
+			break;
+	}
+	if (vma->vm_start <= address)
+		goto good_area;
+	if (!(vma->vm_flags & VM_GROWSDOWN)){ why=1;
+		goto bad_area;}
+	if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur){ why=2;
+		goto bad_area;}
+	vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
+	vma->vm_start = (address & PAGE_MASK);
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+	if (error_code & 2) {
+		if (!(vma->vm_flags & VM_WRITE)){why = 0x30;
+			goto bad_area;}
+	} else {
+		if (!(vma->vm_flags & (VM_READ | VM_EXEC))){why = 0x32;
+			goto bad_area;}
+	}
+	if (error_code & _PAGE_PRESENT) {
+		do_wp_page(current, vma, address, error_code & 2);
+		return;
+	}
+	do_no_page(current, vma, address, error_code & 2);
+	return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+	if (error_code & _PAGE_USER) {
+		current->tss.error_code = error_code;
+		current->tss.trap_no = 14;
+printk("Bad accesses (why = %d) at %08lX (pc=%08lX, lr=%08lX)\n",
+	 why, address, regs->uregs[15], regs->uregs[14]);
+		send_sig(SIGSEGV, current, 1);
+		return;
+	}
+	
+	if(address < PAGE_SIZE)
+		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+	else
+		printk(KERN_ALERT "Unable to handle kernel paging request");
+	printk(" at virtual address %08lx\n", address);
+	die_if_kernel("Oops", regs, error_code, 0, SIGSEGV);
+}
+
+/*
+ * Handle a data abort - we get a range of addresses to handle
+ * (from the load/store multiple)
+ */
+asmlinkage int do_dataabort (unsigned long min_addr, unsigned long max_addr, int mode, unsigned long *regs)
+{
+	unsigned long error_code;
+	int done = 0;
+	pte_t *pte;
+
+	IDEBUG(debug (min_addr, max_addr, mode, regs));
+	
+	if ((min_addr >= LOGICAL_END || max_addr >= LOGICAL_END) && (mode & 3) == 0)
+	{
+		send_sig (SIGSEGV, current, 1);
+		return 1;
+	}
+	
+	error_code = ((mode & 0x20) ? _PAGE_RW : 0) | ((mode & 3) ? 0 : _PAGE_USER);
+
+	pte = pte_offset (pmd_offset (pgd_offset (current->mm, min_addr), min_addr), min_addr);
+
+	if (pte_present(*pte)) {
+		if ((error_code & _PAGE_USER) && !pte_read(*pte)) {
+			printk("Process (%s, %d) tried to access reserved mem %08lx mode %d (pc=%08lx, lr=%08lx)\n",
+				current->comm, current->pid, min_addr, mode, regs[15], regs[14]);
+			send_sig(SIGSEGV, current, 1);
+			return 1;
+		}
+
+		if ((mode & 0x700)==0x100 && pte_young(*pte)) {
+			printk("Page %08lx (%08lx) in but got abort (pc=%08lx) - undefined instr\n",
+				min_addr, pte_val(*pte), regs[15]);
+			return 0;
+		}
+
+		if (!pte_young (*pte)) {
+			*pte = pte_mkyoung (*pte);
+			done = 1;
+		}
+
+		if ((error_code & _PAGE_RW) && pte_write(*pte) && !pte_dirty (*pte)) {
+			*pte = pte_mkdirty (*pte);
+			done = 1;
+		}
+
+		if (done) {
+			invalidate ();
+			return 1;
+		}
+
+		error_code |= _PAGE_PRESENT;
+	}
+
+	do_page_fault1(min_addr, error_code, (struct pt_regs *)regs);
+
+	if ((min_addr ^ max_addr) >> PAGE_SHIFT) {
+		pte = pte_offset (pmd_offset (pgd_offset (current->mm, max_addr), max_addr), max_addr);
+		if (pte_present (*pte)) {
+			if (!pte_young (*pte)) {
+				*pte = pte_mkyoung (*pte);
+				done = 1;
+			}
+
+			if ((error_code & _PAGE_RW) && pte_write(*pte) && !pte_dirty (*pte)) {
+				*pte = pte_mkdirty (*pte);
+				done = 1;
+			}
+
+			if (done) {
+				invalidate ();
+				return 1;
+			}
+
+			error_code |= _PAGE_PRESENT;
+		}
+		else
+			error_code &= ~_PAGE_PRESENT;
+		do_page_fault1(max_addr, error_code, (struct pt_regs *)regs);
+	}
+
+	invalidate();
+	return 1;
+}
diff -urNwbB linux/arch/arm/mm/init.c linux.arm/arch/arm/mm/init.c
--- linux/arch/arm/mm/init.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/init.c	Sun Mar  3 13:08:36 1996
@@ -0,0 +1,286 @@
+/*
+ *  linux/arch/arm/mm/init.c
+ *
+ *  Copyright (C) 1995, 1996  Russell King
+ */
+
+#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 <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+
+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);
+
+unsigned long low_memory;
+int page_nr    = 256;
+pgd_t swapper_pg_dir[1024];
+int page_shift = 15;
+
+extern void memfastset (void *, unsigned long n, size_t l);
+
+/*
+ * 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.
+ */
+#if 0
+pte_t * __bad_pagetable(void)
+{
+	extern char empty_bad_page_table[PAGE_SIZE];
+
+	__asm__ __volatile__("cld ; rep ; stosl":
+		:"a" (pte_val(BAD_PAGE)),
+		 "D" ((long) empty_bad_page_table),
+		 "c" (PAGE_SIZE/4)
+		:"di","cx");
+	return (pte_t *) empty_bad_page_table;
+}
+#endif
+char *empty_bad_page;
+pte_t __bad_page(void)
+{
+        memfastset(empty_bad_page, 0, PAGE_SIZE);
+	return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED));
+}
+
+char *empty_zero_page;
+unsigned long __zero_page(void)
+{
+        memfastset(empty_zero_page, 0, PAGE_SIZE);
+	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_OFFSET) >> PAGE_SHIFT;
+	while (i-- > 0) {
+		total++;
+		if (mem_map[i].reserved)
+			reserved++;
+		else if (!mem_map[i].count)
+			free++;
+		else
+			shared += mem_map[i].count-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);
+
+unsigned long map_screen_mem(int log_start,int update)
+{
+	/* This routine needs more work to make it dynamically release/allocate mem! */
+	unsigned long address;
+	int i;
+	static int updated=0;
+#if 0
+	unsigned long screen_size = 256*1024;
+#else
+	unsigned long screen_size = 480*1024;
+#endif
+	pgd_t *pg_dir;
+	pmd_t *pm_dir;
+	pte_t *pt_entry;
+
+	if(updated)
+		return 0;
+
+	address=LOGICAL_END;
+
+	for(i = LOGICAL_END - screen_size; i<LOGICAL_END; i+=PAGE_SIZE)
+	{
+		pg_dir = swapper_pg_dir + (i >> PGDIR_SHIFT);
+		pm_dir = pmd_offset(pg_dir, i);
+		pt_entry = pte_offset(pm_dir, i);
+		if(i>=log_start)
+		{
+			*pt_entry = mk_pte(address, __pgprot(_PAGE_PRESENT|_PAGE_USER|_PAGE_RW|_PAGE_ACCESSED));
+			address+=PAGE_SIZE;
+		}
+		else
+			*pt_entry = mk_pte(0, __pgprot(0));
+	}
+	if(update)
+	{
+
+		for(; address<(LOGICAL_END + screen_size); address += PAGE_SIZE)
+		{
+			mem_map[MAP_NR(address)].count = 1;
+			free_page(address);
+		}
+		updated=1;
+		invalidate();
+	}
+	return address;
+}
+
+/*
+ * 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)
+{
+	extern int page_nr;
+	int address;
+	int i;
+	pgd_t *pg_dir;
+	pmd_t *pm_dir;
+	pte_t *pt_entry;
+
+	page_nr = MAP_NR(end_mem);
+/* We appear to need this */
+	current = &init_task;
+	
+	pg_dir = swapper_pg_dir;
+	pm_dir = pmd_offset(pg_dir, 0);
+	pt_entry = pte_offset(pm_dir, 0);
+
+	/* Insert mappings for the screen memory */
+	for(i=0; i<LOGICAL_END; i+=PAGE_SIZE)
+	{
+		pt_entry[i>>PAGE_SHIFT] = mk_pte(0, __pgprot(0));
+	}
+#if 0
+	address = map_screen_mem(0x02000000 - 256*1024, 0);
+#else
+	address = map_screen_mem(LOGICAL_END - 480*1024, 0);
+#endif
+
+	/* Allocate zero page */
+	for(i=0; i<32768>>PAGE_SHIFT; i++)
+	{
+		pt_entry[i] = mk_pte(address, PAGE_READONLY);
+		address += PAGE_SIZE;
+	}
+
+	/* Allocate kernel code pages */
+	for(i=0x01800000>>PAGE_SHIFT; address < start_mem; i++)
+	{
+		pt_entry[i] = mk_pte(address, PAGE_SHARED);
+		address += PAGE_SIZE;
+	}
+
+	empty_bad_page = (unsigned char *)address;
+	address += PAGE_SIZE;
+	empty_zero_page = (unsigned char *)address;
+	address += PAGE_SIZE;
+
+	invalidate();
+	return free_area_init(address, 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;
+	int i;
+	extern void sound_init(void);
+
+	end_mem &= PAGE_MASK;
+	high_memory = end_mem;
+
+	/* mark usable pages in the mem_map[] */
+	start_mem = PAGE_ALIGN(start_mem);
+
+	for(i=0; i<MAP_NR(start_mem); i++)
+	{
+		mem_map[i].reserved = 1;
+		mem_map[i].count = 0;
+	}
+
+	while(i < MAP_NR(end_mem))
+	{
+		mem_map[i].reserved = 0;
+		mem_map[i++].count = 0;
+	}
+
+#ifdef CONFIG_SCSI
+	scsi_mem_init(high_memory);
+#endif
+	for (tmp = LOGICAL_END ; tmp < high_memory ; tmp += PAGE_SIZE) {
+		if (mem_map[MAP_NR(tmp)].reserved) {
+			if (tmp < LOGICAL_END + (256*1024+32*1024))
+				reservedpages++;
+			else if (tmp < ((unsigned long) &etext - 0x01800000 + 0x02048000))
+				codepages++;
+			else
+				datapages++;
+			continue;
+		}
+		mem_map[MAP_NR(tmp)].count = 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 - LOGICAL_END) >> 10,
+		codepages << (PAGE_SHIFT-10),
+		reservedpages << (PAGE_SHIFT-10),
+		datapages << (PAGE_SHIFT-10));
+#ifdef CONFIG_SOUND
+	sound_init();
+#endif
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+	int i;
+
+	i = MAP_NR(high_memory);
+	val->totalram = 0;
+	val->sharedram = 0;
+	val->freeram = nr_free_pages << PAGE_SHIFT;
+	val->bufferram = buffermem;
+	while (i-- > 0)  {
+		if (mem_map[i].reserved)
+			continue;
+		val->totalram++;
+		if (!mem_map[i].count)
+			continue;
+		val->sharedram += mem_map[i].count-1;
+	}
+	val->totalram <<= PAGE_SHIFT;
+	val->sharedram <<= PAGE_SHIFT;
+	return;
+}
diff -urNwbB linux/drivers/net/ppp.c linux.arm/drivers/net/ppp.c
--- linux/drivers/net/ppp.c	Sun Mar  3 11:42:14 1996
+++ linux.arm/drivers/net/ppp.c	Sat Feb 24 09:37:08 1996
@@ -6,7 +6,7 @@
  *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
  *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
  *
- *  ==FILEVERSION 4==
+ *  ==FILEVERSION 7==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -67,13 +67,8 @@
  *   released under the GNU General Public License Version 2.
  */
 
+#include <linux/autoconf.h>
 #include <linux/module.h>
-#ifdef MODULE
-#include <linux/version.h>
-#define STATIC
-#else
-#define STATIC static
-#endif /* def MODULE */
 
 #include <endian.h>
 #include <linux/kernel.h>
@@ -132,8 +127,8 @@
 #define PPP_LQR 0xc025  /* Link Quality Reporting Protocol */
 #endif
 
-STATIC int ppp_register_compressor (struct compressor *cp);
-STATIC void ppp_unregister_compressor (struct compressor *cp);
+static int ppp_register_compressor (struct compressor *cp);
+static void ppp_unregister_compressor (struct compressor *cp);
 
 /*
  * Local functions
@@ -177,8 +172,8 @@
  * Parameters which may be changed via insmod.
  */
 
-STATIC int  flag_time = OPTIMIZE_FLAG_TIME;
-STATIC int  max_dev   = PPP_MAX_DEV;
+static int  flag_time = OPTIMIZE_FLAG_TIME;
+static int  max_dev   = PPP_MAX_DEV;
 
 /*
  * The "main" procedure to the ppp device
@@ -210,10 +205,10 @@
 			   unsigned len, struct sk_buff *skb);
 
 #else /* The 1.3.x kernel is here */
-#define get_long_user(addr)	    get_user(((long *) addr))
+#define get_long_user(addr)	    get_user(((int *) addr))
 #define get_int_user(addr)	    ((int) get_user(((int *) addr)))
 #define put_byte_user(val,addr)	    put_user((val),((u_char *) (addr)))
-#define put_long_user(val,addr)	    put_user((val),((long *) (addr)))
+#define put_long_user(val,addr)	    put_user((val),((int *) (addr)))
 
 static int ppp_dev_header (sk_buff *, struct device *, unsigned short,
 			   void *, void *, unsigned);
@@ -542,6 +537,14 @@
 	ppp->sc_rc_state = NULL;
 }
 
+static struct symbol_table ppp_syms = {
+#include <linux/symtab_begin.h>
+	X(ppp_register_compressor),
+	X(ppp_unregister_compressor),
+	X(ppp_crc16_table),
+#include <linux/symtab_end.h>
+};
+
 /* called at boot/load time for each ppp device defined in the kernel */
 
 #ifndef MODULE
@@ -552,26 +555,19 @@
 	int    answer = 0;
 
 	if (first_time) {
-		static struct symbol_table ppp_syms = {
-#include <linux/symtab_begin.h>
-			X(ppp_register_compressor),
-			X(ppp_unregister_compressor),
-			X(ppp_crc16_table),
-#include <linux/symtab_end.h>
-		};
-
 		first_time = 0;
 		answer     = ppp_first_time();
 		if (answer == 0)
 			(void) register_symtab (&ppp_syms);
 	}
+	if (answer)
+		return answer;
 /*
- * Un-register the devices defined at the start of the system. They will
- * be added when they are needed again. The first device just gets us into
- * this code to register the handlers.
+	 * Return "not found", so that dev_init() will unlink
+	 * the placeholder device entry for us.
  */
-	unregister_netdev (dev);
-	return answer;
+	return ENODEV;
+
 }
 #endif
 
@@ -1851,8 +1847,8 @@
 				if (ppp->flags & SC_DEBUG)
 					printk (KERN_DEBUG
 						"ppp_tty_read: no data "
-						"(EWOULDBLOCK)\n");
-				return -EWOULDBLOCK;
+						"(EAGAIN)\n");
+				return -EAGAIN;
 			}
 			current->timeout = 0;
 
@@ -3364,7 +3360,7 @@
 
 		status = register_netdev (dev);
 		if (status == 0) {
-			printk ("registered device %s\n", dev->name);
+			printk (KERN_INFO "registered device %s\n", dev->name);
 			return (ppp);
 		}
 
@@ -3470,7 +3466,7 @@
 	return (struct compressor *) 0;
 }
 
-STATIC int ppp_register_compressor (struct compressor *cp)
+static int ppp_register_compressor (struct compressor *cp)
 {
 	struct compressor_link *new;
 	unsigned long flags;
@@ -3497,7 +3493,7 @@
 	return 0;
 }
 
-STATIC void ppp_unregister_compressor (struct compressor *cp)
+static void ppp_unregister_compressor (struct compressor *cp)
 {
 	struct compressor_link *prev = (struct compressor_link *) 0;
 	struct compressor_link *lnk;
@@ -3527,7 +3523,6 @@
  *************************************************************/
 
 #ifdef MODULE
-char kernel_version[] = UTS_RELEASE;
 
 int
 init_module(void)
@@ -3539,6 +3534,8 @@
 	if (status != 0)
 		printk (KERN_INFO
 		       "PPP: ppp_init() failure %d\n", status);
+	else
+		(void) register_symtab (&ppp_syms);
 	return (status);
 }
 
@@ -3549,11 +3546,10 @@
 	ppp_ctrl_t *ctl, *next_ctl;
 	struct device *dev;
 	struct ppp *ppp;
-	int busy_flag = MOD_IN_USE;
+	int busy_flag = 0;
 /*
  * Ensure that the devices are not in operation.
  */
-	if (!busy_flag) {
 		ctl = ppp_list;
 		while (ctl) {
 			ppp = ctl2ppp (ctl);
@@ -3569,7 +3565,6 @@
 			}
 			ctl = ctl->next;
 		}
-	}
 /*
  * Ensure that there are no compressor modules registered
  */
@@ -3593,10 +3588,6 @@
 	else
 		printk (KERN_INFO
 		       "PPP: ppp line discipline successfully unregistered\n");
-/*
- * Remove the symbol definitions
- */
-	(void) register_symtab ((struct symbol_table *) 0);
 /*
  * De-register the devices so that there is no problem with them
  */	
diff -urNwbB linux/drivers/net/slhc.c linux.arm/drivers/net/slhc.c
--- linux/drivers/net/slhc.c	Sun Mar  3 11:39:32 1996
+++ linux.arm/drivers/net/slhc.c	Sun Feb 11 09:32:45 1996
@@ -134,7 +134,7 @@
 			kfree((unsigned char *)comp);
 			return NULL;
 		}
-		memset(comp->tstate, 0, rslots * sizeof(struct cstate));
+		memset(comp->tstate, 0, tslots * sizeof(struct cstate));
 		comp->tslot_limit = tslots - 1;
 	}
 
diff -urNwbB linux/fs/exec.c linux.arm/fs/exec.c
--- linux/fs/exec.c	Sun Mar  3 11:40:33 1996
+++ linux.arm/fs/exec.c	Sun Feb 11 13:50:43 1996
@@ -21,6 +21,11 @@
  * trying until we recognize the file or we run out of supported binary
  * formats. 
  */
+/*
+ * Russell King: changes for ARM architecture added in aout_core_dump.
+ *    Sone of the various ARM depencies in load_aout_* will eventually
+ *    disappear however.
+ */
 
 #include <linux/fs.h>
 #include <linux/sched.h>
@@ -249,12 +254,21 @@
 	if (dump.u_dsize != 0) {
 		dump_start = START_DATA(dump);
 		dump_size = dump.u_dsize << PAGE_SHIFT;
+#ifdef __arm__
+		verify_area (VERIFY_READ, (void *)dump_start, dump_size);
+#endif
 		DUMP_WRITE(dump_start,dump_size);
 	}
 /* Now prepare to dump the stack area */
 	if (dump.u_ssize != 0) {
+#ifndef __arm__
+		dump_start = dump.start_stack;
+		dump_size = dump.u_ssize << PAGE_SHIFT;
+#else
 		dump_start = dump.start_stack;
 		dump_size = dump.u_ssize << PAGE_SHIFT;
+		verify_area (VERIFY_READ, (void *)dump_start, dump_size);
+#endif
 		DUMP_WRITE(dump_start,dump_size);
 	}
 /* Finally dump the task struct.  Not be used by gdb, but could be useful */
@@ -346,7 +360,7 @@
 	envp = sp;
 	sp -= argc+1;
 	argv = sp;
-#ifdef __i386__
+#if defined(__i386__) || defined(__arm__)
 	if (!ibcs) {
 		put_user(envp,--sp);
 		put_user(argv,--sp);
@@ -427,14 +441,23 @@
 	while (argc-- > 0) {
 		if (from_kmem == 1)
 			set_fs(new_fs);
+#ifndef __arm__
 		if (!(tmp = get_user(argv+argc)))
+#else
+		if (!(tmp = (from_kmem&3?(char *)*(((unsigned long *)argv)+argc):
+				get_user(argv+argc))))
+#endif
 			panic("VFS: argc is wrong");
 		if (from_kmem == 1)
 			set_fs(old_fs);
 		len=0;		/* remember zero-padding */
 		do {
 			len++;
+#ifndef __arm__
 		} while (get_user(tmp++));
+#else
+		} while (from_kmem&2?*tmp++:get_user(tmp++));
+#endif
 		if (p < len) {	/* this shouldn't happen - 128kB */
 			set_fs(old_fs);
 			return 0;
@@ -453,7 +476,11 @@
 					set_fs(new_fs);
 
 			}
+#ifndef __arm__
 			*(pag + offset) = get_user(tmp);
+#else
+			*(pag + offset) = from_kmem&2?*tmp:get_user(tmp);
+#endif
 		}
 	}
 	if (from_kmem==2)
@@ -785,6 +812,9 @@
 	iput(bprm.inode);
 exec_error1:
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)
+#ifdef __arm__
+		if (bprm.page[i])
+#endif
 		free_page(bprm.page[i]);
 	return(retval);
 }
@@ -813,19 +843,30 @@
 	unsigned long error;
 	unsigned long p = bprm->p;
 	unsigned long fd_offset;
+#ifdef __arm__
+	int mach_type_arm;
+	int old_binary = 0;
+#endif
 
 	ex = *((struct exec *) bprm->buf);		/* exec-header */
 	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && 
+#ifdef __arm__
+	     N_MAGIC(ex) != NMAGIC &&
+#endif
 	     N_MAGIC(ex) != QMAGIC) ||
 	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
 	    bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
 		return -ENOEXEC;
 	}
+#ifdef __arm__
+	if (N_MAGIC(ex) == NMAGIC)
+		old_binary |= 1;
+#endif
 
 	current->personality = PER_LINUX;
 	fd_offset = N_TXTOFF(ex);
 
-#ifdef __i386__
+#if defined (__i386__) || defined (__arm__)
 	if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) {
 		printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n");
 		return -ENOEXEC;
@@ -852,11 +893,20 @@
 	current->mm->mmap = NULL;
 	current->suid = current->euid = current->fsuid = bprm->e_uid;
 	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+#ifndef __arm__
 	if (N_MAGIC(ex) == OMAGIC) {
 		do_mmap(NULL, 0, ex.a_text+ex.a_data,
 			PROT_READ|PROT_WRITE|PROT_EXEC,
 			MAP_FIXED|MAP_PRIVATE, 0);
 		read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data, 0);
+#else
+	mach_type_arm = (N_MACHTYPE(ex) == M_ARM);
+	if (N_MAGIC(ex) == OMAGIC || N_MAGIC(ex) == NMAGIC) {
+		do_mmap(NULL, 0x8000, ex.a_text+ex.a_data,
+			PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_FIXED|MAP_PRIVATE, 0);
+		read_exec(bprm->inode, 32, (char *) 0x8000, ex.a_text+ex.a_data, 0);
+#endif
 	} else {
 		if (ex.a_text & 0xfff || ex.a_data & 0xfff)
 			printk(KERN_NOTICE "executable not page aligned\n");
@@ -898,6 +948,10 @@
 		}
 	}
 beyond_if:
+#ifdef __arm__
+	if (old_binary || !mach_type_arm)
+		printk(KERN_NOTICE "Old binary: %s\n", current->comm);
+#endif
 	if (current->exec_domain && current->exec_domain->use_count)
 		(*current->exec_domain->use_count)--;
 	if (current->binfmt && current->binfmt->use_count)
@@ -926,7 +980,11 @@
 	start_thread(regs, ex.a_entry, p);
 	if (current->flags & PF_PTRACED)
 		send_sig(SIGTRAP, current, 0);
+#ifndef __arm__
 	return 0;
+#else
+	return regs->ARM_r0;
+#endif
 }
 
 
@@ -939,6 +997,10 @@
 	unsigned int bss;
 	unsigned int start_addr;
 	unsigned long error;
+#ifdef __arm__
+	unsigned int offset;
+	int old_library = 0;
+#endif
 	
 	file = current->files->fd[fd];
 	inode = file->f_inode;
@@ -966,6 +1028,7 @@
 	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
 	   this off to get the starting address for the page */
 
+#ifndef __arm__
 	start_addr =  ex.a_entry & 0xfffff000;
 
 	/* Now use mmap to map the library into memory. */
@@ -981,5 +1044,34 @@
 		do_mmap(NULL, start_addr + len, bss-len,
 			PROT_READ|PROT_WRITE|PROT_EXEC,
 			MAP_PRIVATE|MAP_FIXED, 0);
+#else
+	if (N_MAGIC(ex) == QMAGIC) {
+		if ((ex.a_entry & 0x7000) == 0x7000) {
+			old_library = 1;
+			start_addr = PAGE_ALIGN(ex.a_entry);
+			offset = 0x1000;
+		} else {
+			start_addr = ex.a_entry & 0xffff8000;
+			offset = N_TXTOFF(ex);
+		}
+	} else {
+		start_addr = ex.a_entry;
+		offset = (N_MACHTYPE(ex) == M_ARM ? N_TXTOFF(ex) : 0x8000);
+	}
+
+	/* Now use mmap to map the library into memory. */
+	error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
+			PROT_READ | PROT_WRITE | PROT_EXEC,
+			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
+			offset);
+	if (error != start_addr)
+		return error;
+	len = PAGE_ALIGN(ex.a_text + ex.a_data);
+	bss = ex.a_text + ex.a_data + ex.a_bss;
+	if (bss > len)
+		do_mmap(NULL, start_addr + len, bss-len,
+			PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_PRIVATE|MAP_FIXED, 0);
+#endif
 	return 0;
 }
diff -urNwbB linux/fs/isofs/Makefile linux.arm/fs/isofs/Makefile
--- linux/fs/isofs/Makefile	Sun Mar  3 11:30:17 1996
+++ linux.arm/fs/isofs/Makefile	Sun Feb 11 14:09:57 1996
@@ -6,9 +6,17 @@
 # unless it's something special (ie not a .c file).
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
+#
+# Russell King
+#	Changed so that on the arm, we use our own modified rock
+#	ridge extensions.
 
 O_TARGET := isofs.o
+ifndef CONFIG_ARM
 O_OBJS   := namei.o inode.o file.o dir.o util.o rock.o symlink.o
+else
+O_OBJS   := namei.o inode.o file.o dir.o util.o rock-arm.o symlink.o
+endif
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -urNwbB linux/fs/isofs/rock-arm.c linux.arm/fs/isofs/rock-arm.c
--- linux/fs/isofs/rock-arm.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/fs/isofs/rock-arm.c	Sun Feb 11 14:11:02 1996
@@ -0,0 +1,543 @@
+/*
+ *  linux/fs/isofs/rock.c
+ *
+ *  (C) 1992, 1993  Eric Youngdale
+ *
+ *  Rock Ridge Extensions to iso9660
+ *
+ *  Modifed by Russell King 11/02/96
+ *	Allows iso rock ridge extensions to work.
+ */
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include "rock-arm.h"
+
+/* These functions are designed to read the system areas of a directory record
+ * and extract relevant information.  There are different functions provided
+ * depending upon what information we need at the time.  One function fills
+ * out an inode structure, a second one extracts a filename, a third one
+ * returns a symbolic link name, and a fourth one returns the extent number
+ * for the file. */
+
+#define SIG(A,B) ((A << 8) | B)
+
+
+/* This is a way of ensuring that we have something in the system
+   use fields that is compatible with Rock Ridge */
+#define CHECK_SP(FAIL)	       			\
+      if(rr->u.SP.magic[0] != 0xbe) FAIL;	\
+      if(rr->u.SP.magic[1] != 0xef) FAIL;
+
+/* We define a series of macros because each function must do exactly the
+   same thing in certain places.  We use the macros to ensure that everything
+   is done correctly */
+
+#define CONTINUE_DECLS \
+  int cont_extent = 0, cont_offset = 0, cont_size = 0;   \
+  void * buffer = 0
+
+#define CHECK_CE	       			\
+      {cont_extent = isonum_733(rr->u.CE.extent); \
+      cont_offset = isonum_733(rr->u.CE.offset); \
+      cont_size = isonum_733(rr->u.CE.size);}
+
+#define SETUP_ROCK_RIDGE(DE,CHR,LEN)	      		      			\
+  {LEN= (int)(((struct iso_directory_record *)0)->name) + DE->name_len[0];	\
+  if(LEN & 1) LEN++;								\
+  CHR = ((unsigned char *) DE) + LEN;						\
+  LEN = *((unsigned char *) DE) - LEN;}
+
+#define MAYBE_CONTINUE(LABEL,DEV) \
+  {if (buffer) kfree(buffer); \
+  if (cont_extent){ \
+    int block, offset, offset1; \
+    struct buffer_head * bh; \
+    buffer = kmalloc(cont_size,GFP_KERNEL); \
+    if (!buffer) goto out; \
+    block = cont_extent; \
+    offset = cont_offset; \
+    offset1 = 0; \
+    if(ISOFS_BUFFER_SIZE(DEV) == 1024) {     \
+      block <<= 1;    \
+      if (offset >= 1024) block++; \
+      offset &= 1023; \
+      if(offset + cont_size >= 1024) { \
+	  bh = bread(DEV->i_dev, block++, ISOFS_BUFFER_SIZE(DEV)); \
+	  if(!bh) {printk("Unable to read continuation Rock Ridge record\n"); \
+		     kfree(buffer); \
+		     buffer = NULL; } else { \
+	    memcpy(buffer, bh->b_data + offset, 1024 - offset); \
+            brelse(bh); \
+	    offset1 = 1024 - offset; \
+	    offset = 0;} \
+      }  \
+    };     \
+    if(buffer) { \
+      bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
+      if(bh){       \
+        memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \
+        brelse(bh); \
+        chr = (unsigned char *) buffer; \
+        len = cont_size; \
+        cont_extent = 0; \
+        cont_size = 0; \
+        cont_offset = 0; \
+        goto LABEL; \
+      };    \
+    } \
+    printk("Unable to read rock-ridge attributes\n");    \
+  }}
+
+/* This is the inner layer of the get filename routine, and is called
+   for each system area and continuation record related to the file */
+
+int find_rock_ridge_relocation(struct iso_directory_record * de, 
+			       struct inode * inode) {
+  int flag;
+  int len;
+  int retval;
+  unsigned char * chr;
+  CONTINUE_DECLS;
+  flag = 0;
+  
+  /* If this is a '..' then we are looking for the parent, otherwise we
+     are looking for the child */
+  
+  if (de->name[0]==1 && de->name_len[0]==1) flag = 1;
+  /* Return value if we do not find appropriate record. */
+  retval = isonum_733 (de->extent);
+  
+  if (!inode->i_sb->u.isofs_sb.s_rock) return retval;
+
+  SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+  {
+    int rrflag, sig;
+    struct rock_ridge * rr;
+    
+    while (len > 1){ /* There may be one byte for padding somewhere */
+      rr = (struct rock_ridge *) chr;
+      if (rr->len == 0) goto out; /* Something got screwed up here */
+      sig = (chr[0] << 8) + chr[1];
+      chr += rr->len; 
+      len -= rr->len;
+
+      switch(sig){
+      case SIG('R','R'):
+	rrflag = rr->u.RR.flags[0];
+	if (flag && !(rrflag & RR_PL)) goto out;
+	if (!flag && !(rrflag & RR_CL)) goto out;
+	break;
+      case SIG('S','P'):
+	CHECK_SP(goto out);
+	break;
+      case SIG('C','L'):
+#ifdef DEBUG
+	printk("RR: CL\n");
+#endif
+	if (flag == 0) {
+	  retval = isonum_733(rr->u.CL.location);
+	  goto out;
+	};
+	break;
+      case SIG('P','L'):
+#ifdef DEBUG
+	printk("RR: PL\n");
+#endif
+	if (flag != 0) {
+	  retval = isonum_733(rr->u.PL.location);
+	  goto out;
+	};
+	break;
+      case SIG('C','E'):
+	CHECK_CE; /* This tells is if there is a continuation record */
+	break;
+      default:
+	break;
+      }
+    };
+  };
+  MAYBE_CONTINUE(repeat, inode);
+  return retval;
+ out:
+  if(buffer) kfree(buffer);
+  return retval;
+}
+
+int get_rock_ridge_filename(struct iso_directory_record * de,
+			   char ** name, int * namlen, struct inode * inode)
+{
+  int len;
+  unsigned char * chr;
+  CONTINUE_DECLS;
+  char * retname = NULL;
+  int retnamlen = 0, truncate=0;
+ 
+  if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
+
+  SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+  {
+    struct rock_ridge * rr;
+    int sig;
+    
+    while (len > 1){ /* There may be one byte for padding somewhere */
+      rr = (struct rock_ridge *) chr;
+      if (rr->len == 0) goto out; /* Something got screwed up here */
+      sig = (chr[0] << 8) + chr[1];
+      chr += rr->len; 
+      len -= rr->len;
+
+      switch(sig){
+      case SIG('R','R'):
+	if((rr->u.RR.flags[0] & RR_NM) == 0) goto out;
+	break;
+      case SIG('S','P'):
+	CHECK_SP(goto out);
+	break;
+      case SIG('C','E'):
+	CHECK_CE;
+	break;
+      case SIG('N','M'):
+	if (truncate) break;
+	if (rr->u.NM.flags & ~1) {
+	  printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
+	  break;
+	};
+	if (!retname){
+	  retname = (char *) kmalloc (255,GFP_KERNEL);
+	  /* This may be a waste, but we only
+	     need this for a moment.  The layers
+	     that call this function should
+	     deallocate the mem fairly soon
+	     after control is returned */
+
+	  if (!retname) goto out;
+	  *retname = 0; /* Zero length string */
+	  retnamlen = 0;
+	};
+	if((strlen(retname) + rr->len - 5) >= 254) {
+	  truncate = 1;
+	  break;
+	};
+	strncat(retname, rr->u.NM.name, rr->len - 5);
+	retnamlen += rr->len - 5;
+	break;
+      case SIG('R','E'):
+#ifdef DEBUG
+	printk("RR: RE (%x)\n", inode->i_ino);
+#endif
+	if (buffer) kfree(buffer);
+	if (retname) kfree(retname);
+	return -1;
+      default:
+	break;
+      }
+    };
+  }
+  MAYBE_CONTINUE(repeat,inode);
+  if(retname){
+    *name = retname;
+    *namlen = retnamlen;
+    return 1;
+  };
+  return 0;  /* This file did not have a NM field */
+ out:
+  if(buffer) kfree(buffer);
+  if (retname) kfree(retname);
+  return 0;
+}
+
+int parse_rock_ridge_inode(struct iso_directory_record * de,
+			   struct inode * inode){
+  int len;
+  unsigned char * chr;
+  CONTINUE_DECLS;
+
+  if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
+
+  SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+  {
+    int cnt, sig;
+    struct inode * reloc;
+    struct rock_ridge * rr;
+    int rootflag;
+    
+    while (len > 1){ /* There may be one byte for padding somewhere */
+      rr = (struct rock_ridge *) chr;
+      if (rr->len == 0) goto out; /* Something got screwed up here */
+      sig = (chr[0] << 8) + chr[1];
+      chr += rr->len; 
+      len -= rr->len;
+
+      switch(sig){
+      case SIG('R','R'):
+	if((rr->u.RR.flags[0] & 
+ 	    (RR_PX | RR_TF | RR_SL | RR_CL)) == 0) goto out;
+	break;
+      case SIG('S','P'):
+	CHECK_SP(goto out);
+	break;
+      case SIG('C','E'):
+	CHECK_CE;
+	break;
+      case SIG('E','R'):
+	printk("ISO9660 Extensions: ");
+	{ int p;
+	  for(p=0;p<rr->u.ER.len_id;p++) printk("%c",rr->u.ER.data[p]);
+	};
+	  printk("\n");
+	break;
+      case SIG('P','X'):
+	inode->i_mode  = isonum_733(rr->u.PX.mode);
+	inode->i_nlink = isonum_733(rr->u.PX.n_links);
+	inode->i_uid   = isonum_733(rr->u.PX.uid);
+	inode->i_gid   = isonum_733(rr->u.PX.gid);
+	break;
+      case SIG('P','N'):
+	{ int high, low;
+	  high = isonum_733(rr->u.PN.dev_high);
+	  low = isonum_733(rr->u.PN.dev_low);
+	  /*
+	   * The Rock Ridge standard specifies that if sizeof(dev_t) <=4,
+	   * then the high field is unused, and the device number is completely
+	   * stored in the low field.  Some writers may ignore this subtlety,
+	   * and as a result we test to see if the entire device number is
+	   * stored in the low field, and use that.
+	   */
+	  if(MINOR(low) != low && high == 0) {
+	    inode->i_rdev = low;
+	  } else {
+	    inode->i_rdev = MKDEV(high, low);
+	  }
+	};
+	break;
+      case SIG('T','F'):
+      {
+      	struct stamp *times = (struct stamp *) &(rr->u.TF.__times);
+	/* Some RRIP writers incorrectly place ctime in the TF_CREATE field.
+	   Try and handle this correctly for either case. */
+	cnt = 0; /* Rock ridge never appears on a High Sierra disk */
+	if(rr->u.TF.flags & TF_CREATE) 
+	  inode->i_ctime = iso_date(times[cnt++].time, 0);
+	if(rr->u.TF.flags & TF_MODIFY) 
+	  inode->i_mtime = iso_date(times[cnt++].time, 0);
+	if(rr->u.TF.flags & TF_ACCESS) 
+	  inode->i_atime = iso_date(times[cnt++].time, 0);
+	if(rr->u.TF.flags & TF_ATTRIBUTES) 
+	  inode->i_ctime = iso_date(times[cnt++].time, 0);
+	break;
+      }
+      case SIG('S','L'):
+	{int slen;
+	 struct SL_component * slp;
+	 slen = rr->len - 5;
+	 slp = (struct SL_component *)&rr->u.SL.__link;
+	 inode->i_size = 0;
+	 while (slen > 1){
+	   rootflag = 0;
+	   switch(slp->flags &~1){
+	   case 0:
+	     inode->i_size += slp->len;
+	     break;
+	   case 2:
+	     inode->i_size += 1;
+	     break;
+	   case 4:
+	     inode->i_size += 2;
+	     break;
+	   case 8:
+	     rootflag = 1;
+	     inode->i_size += 1;
+	     break;
+	   default:
+	     printk("Symlink component flag not implemented\n");
+	   };
+	   slen -= slp->len + 2;
+	   slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
+
+	   if(slen < 2) break;
+	   if(!rootflag) inode->i_size += 1;
+	 };
+       };
+	break;
+      case SIG('R','E'):
+	printk("Attempt to read inode for relocated directory\n");
+	goto out;
+      case SIG('C','L'):
+#ifdef DEBUG
+	printk("RR CL (%x)\n",inode->i_ino);
+#endif
+	inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) <<
+		inode -> i_sb -> u.isofs_sb.s_log_zone_size;
+	reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent);
+	inode->i_mode = reloc->i_mode;
+	inode->i_nlink = reloc->i_nlink;
+	inode->i_uid = reloc->i_uid;
+	inode->i_gid = reloc->i_gid;
+	inode->i_rdev = reloc->i_rdev;
+	inode->i_size = reloc->i_size;
+	inode->i_atime = reloc->i_atime;
+	inode->i_ctime = reloc->i_ctime;
+	inode->i_mtime = reloc->i_mtime;
+	iput(reloc);
+	break;
+      default:
+	break;
+      }
+    };
+  }
+  MAYBE_CONTINUE(repeat,inode);
+  return 0;
+ out:
+  if(buffer) kfree(buffer);
+  return 0;
+}
+
+
+/* Returns the name of the file that this inode is symlinked to.  This is
+   in malloc'd memory, so it needs to be freed, once we are through with it */
+
+char * get_rock_ridge_symlink(struct inode * inode)
+{
+  unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+  unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
+  struct buffer_head * bh;
+  unsigned char * pnt;
+  void * cpnt = NULL;
+  char * rpnt;
+  struct iso_directory_record * raw_inode;
+  CONTINUE_DECLS;
+  int block;
+  int sig;
+  int rootflag;
+  int len;
+  unsigned char * chr;
+  struct rock_ridge * rr;
+  
+  if (!inode->i_sb->u.isofs_sb.s_rock)
+    panic("Cannot have symlink with high sierra variant of iso filesystem\n");
+
+  rpnt = 0;
+  
+  block = inode->i_ino >> bufbits;
+  if (!(bh=bread(inode->i_dev,block, bufsize))) {
+    printk("unable to read i-node block");
+    return NULL;
+  };
+  
+  pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1));
+  
+  raw_inode = ((struct iso_directory_record *) pnt);
+  
+  if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){
+    int frag1, offset;
+    
+    offset = (inode->i_ino & (bufsize - 1));
+    frag1 = bufsize - offset;
+    cpnt = kmalloc(*pnt,GFP_KERNEL);
+    if(!cpnt) return NULL;
+    memcpy(cpnt, bh->b_data + offset, frag1);
+    brelse(bh);
+    if (!(bh = bread(inode->i_dev,++block, bufsize))) {
+      kfree(cpnt);
+      printk("unable to read i-node block");
+      return NULL;
+    };
+    offset += *pnt - bufsize;
+    memcpy((char *)cpnt+frag1, bh->b_data, offset);
+    pnt = ((unsigned char *) cpnt);
+    raw_inode = ((struct iso_directory_record *) pnt);
+  };
+  
+  /* Now test for possible Rock Ridge extensions which will override some of
+     these numbers in the inode structure. */
+  
+  SETUP_ROCK_RIDGE(raw_inode, chr, len);
+  
+ repeat:
+  while (len > 1){ /* There may be one byte for padding somewhere */
+    if (rpnt) break;
+    rr = (struct rock_ridge *) chr;
+    if (rr->len == 0) goto out; /* Something got screwed up here */
+    sig = (chr[0] << 8) + chr[1];
+    chr += rr->len; 
+    len -= rr->len;
+    
+    switch(sig){
+    case SIG('R','R'):
+      if((rr->u.RR.flags[0] & RR_SL) == 0) goto out;
+      break;
+    case SIG('S','P'):
+      CHECK_SP(goto out);
+      break;
+    case SIG('S','L'):
+      {int slen;
+       struct SL_component * slp;
+       slen = rr->len - 5;
+       slp = (struct SL_component *)&rr->u.SL.__link;
+       while (slen > 1){
+	 if (!rpnt){
+	   rpnt = (char *) kmalloc (inode->i_size +1, GFP_KERNEL);
+	   if (!rpnt) goto out;
+	   *rpnt = 0;
+	 };
+	 rootflag = 0;
+	 switch(slp->flags &~1){
+	 case 0:
+	   strncat(rpnt,slp->text, slp->len);
+	   break;
+	 case 2:
+	   strcat(rpnt,".");
+	   break;
+	 case 4:
+	   strcat(rpnt,"..");
+	   break;
+	 case 8:
+	   rootflag = 1;
+	   strcat(rpnt,"/");
+	   break;
+	 default:
+	   printk("Symlink component flag not implemented (%d)\n",slen);
+	 };
+	 slen -= slp->len + 2;
+	 slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
+
+	 if(slen < 2) break;
+	 if(!rootflag) strcat(rpnt,"/");
+       };
+       break;
+     default:
+       break;
+     }
+    };
+  };
+  MAYBE_CONTINUE(repeat,inode);
+  brelse(bh);
+  
+  if (cpnt) {
+    kfree(cpnt);
+    cpnt = NULL;
+  };
+
+  return rpnt;
+ out:
+  if(buffer) kfree(buffer);
+  return 0;
+}
+
+
+
+
+
+
diff -urNwbB linux/fs/isofs/rock-arm.h linux.arm/fs/isofs/rock-arm.h
--- linux/fs/isofs/rock-arm.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/fs/isofs/rock-arm.h	Sun Feb 11 14:11:41 1996
@@ -0,0 +1,114 @@
+/*
+ * altered for arm by Russell King.
+ */
+/* These structs are used by the system-use-sharing protocol, in which the
+   Rock Ridge extensions are embedded.  It is quite possible that other
+   extensions are present on the disk, and this is fine as long as they
+   all use SUSP */
+
+struct SU_SP{
+  unsigned char magic[2];
+  unsigned char skip;
+};
+
+struct SU_CE{
+  char extent[8];
+  char offset[8];
+  char size[8];
+};
+
+struct SU_ER{
+  unsigned char len_id;
+  unsigned char len_des;
+  unsigned char len_src;
+  unsigned char ext_ver;
+  char data[0];
+};
+
+struct RR_RR{
+  char flags[1];
+};
+
+struct RR_PX{
+  char mode[8];
+  char n_links[8];
+  char uid[8];
+  char gid[8];
+};
+
+struct RR_PN{
+  char dev_high[8];
+  char dev_low[8];
+};
+
+
+struct SL_component{
+  unsigned char flags;
+  unsigned char len;
+  char text[0];
+};
+
+struct RR_SL{
+  unsigned char flags;
+  unsigned char __link,__dummy;
+};
+
+struct RR_NM{
+  unsigned char flags;
+  char name[0];
+};
+
+struct RR_CL{
+  char location[8];
+};
+
+struct RR_PL{
+  char location[8];
+};
+
+struct stamp{
+  char time[7];
+};
+
+struct RR_TF{
+  char flags;
+  unsigned char __times[0];
+};
+
+/* These are the bits and their meanings for flags in the TF structure. */
+#define TF_CREATE 1
+#define TF_MODIFY 2
+#define TF_ACCESS 4
+#define TF_ATTRIBUTES 8
+#define TF_BACKUP 16
+#define TF_EXPIRATION 32
+#define TF_EFFECTIVE 64
+#define TF_LONG_FORM 128
+
+struct rock_ridge{
+  char signature[2];
+  unsigned char len;
+  unsigned char version;
+  union{
+    struct SU_SP SP;
+    struct SU_CE CE;
+    struct SU_ER ER;
+    struct RR_RR RR;
+    struct RR_PX PX;
+    struct RR_PN PN;
+    struct RR_SL SL;
+    struct RR_NM NM;
+    struct RR_CL CL;
+    struct RR_PL PL;
+    struct RR_TF TF;
+  } u;
+};
+
+#define RR_PX 1   /* POSIX attributes */
+#define RR_PN 2   /* POSIX devices */
+#define RR_SL 4   /* Symbolic link */
+#define RR_NM 8   /* Alternate Name */
+#define RR_CL 16  /* Child link */
+#define RR_PL 32  /* Parent link */
+#define RR_RE 64  /* Relocation directory */
+#define RR_TF 128 /* Timestamps */
diff -urNwbB linux/fs/msdos/inode.c linux.arm/fs/msdos/inode.c
--- linux/fs/msdos/inode.c	Sun Mar  3 11:36:55 1996
+++ linux.arm/fs/msdos/inode.c	Sun Feb 11 14:13:20 1996
@@ -2,6 +2,10 @@
  *  linux/fs/msdos/inode.c
  *
  *  Written 1992,1993 by Werner Almesberger
+ *
+ *  Modified by Russell King 11/02/96
+ *	Some short words on the msdos format are not on a short word boundary -
+ *	requires special handling.
  */
 
 #ifdef MODULE
@@ -224,7 +228,12 @@
 #define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
     /* don't divide by zero */
 
+#ifndef __arm__
 	logical_sector_size = CF_LE_W(*(unsigned short *) &b->sector_size);
+#else
+#define GET_WORD(x) (((unsigned char *)(x))[0] | (((unsigned char *)(x))[1]<<8))
+	logical_sector_size = CF_LE_W(GET_WORD(&b->sector_size));
+#endif
 	sector_mult = logical_sector_size >> SECTOR_BITS;
 	MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
 	MSDOS_SB(sb)->fats = b->fats;
@@ -237,9 +246,15 @@
 	MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
 	    MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
 	    sector_mult);
+#ifndef __arm__
 	data_sectors = (CF_LE_W(*((unsigned short *) &b->sectors)) ?
 	    CF_LE_W(*((unsigned short *) &b->sectors)) :
 	    CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(sb)->data_start;
+#else
+	data_sectors = (CF_LE_W(GET_WORD(&b->sectors)) ?
+	    CF_LE_W(GET_WORD(&b->sectors)) :
+	    CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(sb)->data_start;
+#endif
 	error = !b->cluster_size || !sector_mult;
 	if (!error) {
 		MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
diff -urNwbB linux/fs/proc/array.c linux.arm/fs/proc/array.c
--- linux/fs/proc/array.c	Sun Mar  3 11:40:33 1996
+++ linux.arm/fs/proc/array.c	Sun Feb 11 14:20:29 1996
@@ -25,6 +25,10 @@
  *
  * Bruno Haible      :  remove 4K limit for the maps file
  * <haible@ma2s2.mathematik.uni-karlsruhe.de>
+ *
+ * Modifications:
+ * Russell King      :  modified for the ARM processor.
+ *                      <rmk92@ecs.soton.ac.uk>
  */
 
 #include <linux/types.h>
@@ -393,12 +397,38 @@
 			return eip;
 		ebp = *(unsigned long *) ebp;
 	} while (count++ < 16);
+#elif defined (__arm__)
+	unsigned long fp, pc;
+	unsigned long stack_page;
+	int count = 0;
+
+	if (!p || p == current || p->state == TASK_RUNNING)
+		return 0;
+	stack_page = p->kernel_stack_page;
+	if (!stack_page)
+		return 0;
+	fp = p->tss.sregs[11-4]; /* get current fp (should be schedule) */
+	do {
+		if (fp < stack_page || fp > 4092+stack_page)
+			return 0;
+		pc = ((*(unsigned long *)fp)-16) & ~0xfc000003;
+		if ((void *)pc != sleep_on &&
+		    (void *)pc != interruptible_sleep_on &&
+		    (void *)pc != schedule)
+			return pc;
+		fp = *(unsigned long *) (fp - 12);
+	} while (count ++ < 16);
 #endif
 	return 0;
 }
 
+#ifndef __arm__
 #define	KSTK_EIP(stack)	(((unsigned long *)stack)[1019])
 #define	KSTK_ESP(stack)	(((unsigned long *)stack)[1022])
+#else
+#define KSTK_EIP(stack) (((unsigned long *)stack)[1022])
+#define KSTK_ESP(stack) (((unsigned long *)stack)[1020])
+#endif
 
 static int get_stat(int pid, char * buffer)
 {
@@ -473,7 +503,11 @@
 		tsk->it_real_value,
 		tsk->start_time,
 		vsize,
+#ifndef __arm__
 		tsk->mm ? tsk->mm->rss : 0, /* you might want to shift this left 3 */
+#else
+		tsk->mm ? tsk->mm->rss * 8 : 0,
+#endif
 		tsk->rlim ? tsk->rlim[RLIMIT_RSS].rlim_cur : 0,
 		tsk->mm ? tsk->mm->start_code : 0,
 		tsk->mm ? tsk->mm->end_code : 0,
@@ -583,7 +617,11 @@
 				trs += pages;	/* text */
 			else if (vma->vm_flags & VM_GROWSDOWN)
 				drs += pages;	/* stack */
+#ifndef __arm__
 			else if (vma->vm_end > 0x60000000)
+#else
+			else if (vma->vm_end > 0x00c00000)
+#endif
 				lrs += pages;	/* library */
 			else
 				drs += pages;
@@ -594,6 +632,99 @@
 		       size, resident, share, trs, lrs, drs, dt);
 }
 
+#ifdef __arm__
+/*
+ * on the arm, it is useful to be able to look at the current memory mapping
+ * for a process
+ */
+
+static int mmaps_pgd(pgd_t *pgd, char *line, int lineno)
+{
+	pmd_t *pmd;
+	pte_t *pte;
+	int len;
+
+	if (pgd_none (*pgd) || pgd_bad (*pgd))
+		return 0;
+	pmd = pmd_offset (pgd, lineno << PAGE_SHIFT);
+
+	if (pmd_none (*pmd) || pmd_bad (*pmd))
+		return 0;
+	pte = pte_offset (pmd, lineno << PAGE_SHIFT);
+
+	if (pte_none (*pte))
+		return 0;
+
+	len = sprintf (line, "%08X = %08lX ", lineno << PAGE_SHIFT, pte_val (*pte));
+	if (pte_present (*pte))
+		len += sprintf (line + len, "[%c%c%c%c%c%c] %c%d\n",
+			pte_cow (*pte)   ? 'C' : ' ',
+			pte_young (*pte) ? 'Y' : ' ',
+			pte_dirty (*pte) ? 'D' : ' ',
+			pte_exec (*pte)  ? 'E' : ' ',
+			pte_write (*pte) ? 'W' : ' ',
+			pte_read (*pte)  ? 'R' : ' ',
+			mem_map[MAP_NR(pte_page(*pte))].reserved?'R':' ',
+			mem_map[MAP_NR(pte_page(*pte))].count);
+	else
+		len += sprintf (line + len, "[S           ]\n");
+	return len;
+}
+
+static int read_mmaps (int pid, struct file *file, char *buf, int count)
+{
+	struct task_struct **p = get_task (pid);
+	char *destptr;
+	loff_t lineno;
+	int column;
+
+	if (!p || !*p)
+		return -EINVAL;
+
+	if (count == 0)
+		return 0;
+
+	lineno = file->f_pos >> 10;
+	column = file->f_pos & 1023;
+
+	destptr = buf;
+
+	while (count && lineno < (LOGICAL_END >> PAGE_SHIFT)) {
+		pgd_t *pgd = pgd_offset ((*p)->mm, lineno << PAGE_SHIFT);
+		char line[1024];
+		int i, len;
+
+		len = mmaps_pgd (pgd, line, lineno);
+		if (len == 0) {
+			lineno ++;
+			continue;
+		}
+
+		if (len < 0 || column >= len) {
+			lineno ++;
+			column = 0;
+			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;
+			lineno ++;
+		}
+	}
+
+	file->f_pos = (lineno << 10) + column;
+
+	return destptr - buf;
+}
+#endif
 /*
  * The way we support synthetic files > 4K
  * - without storing their contents in some buffer and
@@ -885,6 +1016,8 @@
 	switch (type) {
 		case PROC_PID_MAPS:
 			return read_maps(pid, file, buf, count);
+		case PROC_PID_MAPS+1:
+			return read_mmaps(pid, file, buf, count);
 	}
 	return -EINVAL;
 }
diff -urNwbB linux/fs/proc/base.c linux.arm/fs/proc/base.c
--- linux/fs/proc/base.c	Sun Mar  3 11:30:18 1996
+++ linux.arm/fs/proc/base.c	Sun Feb 11 09:32:47 1996
@@ -134,6 +134,14 @@
 		0, &proc_array_inode_operations,
 		NULL, proc_pid_fill_inode,
 	});
+#ifdef __arm__
+	proc_register(&proc_pid, &(struct proc_dir_entry) {
+		PROC_PID_MAPS+1, 5, "mmaps",
+		S_IFIFO | S_IRUGO, 1, 0, 0,
+		0, &proc_arraylong_inode_operations,
+		NULL, proc_pid_fill_inode,
+	});
+#endif
 	proc_register(&proc_pid, &(struct proc_dir_entry) {
 		PROC_PID_MAPS, 4, "maps",
 		S_IFIFO | S_IRUGO, 1, 0, 0,
diff -urNwbB linux/fs/proc/inode.c linux.arm/fs/proc/inode.c
--- linux/fs/proc/inode.c	Sun Mar  3 11:36:57 1996
+++ linux.arm/fs/proc/inode.c	Sun Feb 11 09:32:47 1996
@@ -229,6 +229,9 @@
 			inode->i_op = &proc_array_inode_operations;
 			return;
 		case PROC_PID_MAPS:
+#ifdef __arm__
+		case PROC_PID_MAPS+1:
+#endif
 			inode->i_mode = S_IFIFO | S_IRUGO;
 			inode->i_op = &proc_arraylong_inode_operations;
 			return;
diff -urNwbB linux/fs/smbfs/proc.c linux.arm/fs/smbfs/proc.c
--- linux/fs/smbfs/proc.c	Sun Mar  3 11:31:18 1996
+++ linux.arm/fs/smbfs/proc.c	Sun Feb 11 09:32:48 1996
@@ -23,7 +23,11 @@
 #include <asm/segment.h>
 #include <asm/string.h>
 
+#ifndef __arm__
 #define ARCH i386
+#else
+#define ARCH arm
+#endif
 #define SMB_VWV(packet)  ((packet) + SMB_HEADER_LEN)
 #define SMB_CMD(packet)  ((packet)[8])
 #define SMB_WCT(packet)  ((packet)[SMB_HEADER_LEN - 1])
@@ -50,6 +54,9 @@
 {
 #if (ARCH == i386)
         *((word *)p) = data;
+#elif (ARCH == arm)
+	p[0] = data & 0x00ffU;
+	p[1] = data >> 8;
 #else
 	p[0] = data & 0x00ffU;
 	p[1] = (data & 0xff00U) >> 8;
@@ -63,6 +70,8 @@
 {
 #if (ARCH == i386)
 	*data = *(word *)p;
+#elif (ARCH == arm)
+	*data = (word) p[0] | p[1] << 8;
 #else
 	*data = (word) p[0] | p[1] << 8;
 #endif 
diff -urNwbB linux/fs/smbfs/sock.c linux.arm/fs/smbfs/sock.c
--- linux/fs/smbfs/sock.c	Sun Mar  3 11:31:19 1996
+++ linux.arm/fs/smbfs/sock.c	Sun Feb 11 09:32:49 1996
@@ -277,7 +277,11 @@
         }
 
         server->rcls = *((unsigned char *)(server->packet+9));
+#ifndef __arm__
         server->err  = *((unsigned short *)(server->packet+11));
+#else
+	server->err  = WVAL(server->packet, 11);
+#endif
 
         if (server->rcls != 0) {
                 DPRINTK("smb_receive: rcls=%d, err=%d\n",
diff -urNwbB linux/include/asm-arm/a.out.h linux.arm/include/asm-arm/a.out.h
--- linux/include/asm-arm/a.out.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/a.out.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,30 @@
+#ifndef __ARM_A_OUT_H__
+#define __ARM_A_OUT_H__
+
+struct exec
+{
+  unsigned long a_info;		/* Use macros N_MAGIC, etc for access */
+  unsigned a_text;		/* length of text, in bytes */
+  unsigned a_data;		/* length of data, in bytes */
+  unsigned a_bss;		/* length of uninitialized data area for file, in bytes */
+  unsigned a_syms;		/* length of symbol table data in file, in bytes */
+  unsigned a_entry;		/* start address */
+  unsigned a_trsize;		/* length of relocation info for text, in bytes */
+  unsigned a_drsize;		/* length of relocation info for data, in bytes */
+};
+
+#define N_TXTADDR(a)	0x8000
+
+#define N_TRSIZE(a)	((a).a_trsize)
+#define N_DRSIZE(a)	((a).a_drsize)
+#define N_SYMSIZE(a)	((a).a_syms)
+
+#define M_ARM 103
+
+#ifdef __KERNEL__
+
+#define STACK_TOP	24*1024*1024
+
+#endif
+
+#endif /* __A_OUT_GNU_H__ */
diff -urNwbB linux/include/asm-arm/arcaudio.h linux.arm/include/asm-arm/arcaudio.h
--- linux/include/asm-arm/arcaudio.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/arcaudio.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,51 @@
+/* 
+ * arcaudio.h
+ *
+ */
+
+#ifndef _LINUX_ARCAUDIO_H
+#define _LINUX_ARCAUDIO_H
+
+#define ARCAUDIO_MAXCHANNELS	8
+
+enum ch_type
+{
+  ARCAUDIO_NONE,			/* No sound (muted) */
+  ARCAUDIO_8BITSIGNED,			/* signed 8 bits per samples */
+  ARCAUDIO_8BITUNSIGNED,		/* unsigned 8 bits per samples */
+  ARCAUDIO_16BITSIGNED,			/* signed 16 bits per samples (little endian) */
+  ARCAUDIO_16BITUNSIGNED,		/* unsigned 16 bits per samples (little endian) */
+  ARCAUDIO_LOG				/* Vidc Log */
+};
+
+/* 
+ * Global information
+ */
+struct arcaudio
+{
+  int		sample_rate;		/* sample rate (Hz) */
+  int		num_channels;		/* number of channels */
+  int		volume;			/* overall system volume */
+};
+
+/* 
+ * Per channel information
+ */
+struct arcaudio_channel
+{
+  int		stereo_position;	/* Channel position */
+  int		channel_volume;		/* Channel volume */
+  enum ch_type	channel_type;		/* Type of channel */
+  int		buffer_size;		/* Size of channel buffer */
+};
+
+/* IOCTLS */
+#define ARCAUDIO_GETINFO	0x6101
+#define ARCAUDIO_SETINFO	0x6102
+#define ARCAUDIO_GETCHANNELINFO	0x6111
+#define ARCAUDIO_SETCHANNELINFO	0x6112
+#define ARCAUDIO_GETOPTS	0x61f0
+#define ARCAUDIO_SETOPTS	0x61f1
+#define  ARCAUDIO_OPTSPKR	1<<0
+
+#endif
diff -urNwbB linux/include/asm-arm/assembler.h linux.arm/include/asm-arm/assembler.h
--- linux/include/asm-arm/assembler.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/assembler.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,141 @@
+/*
+ * linux/asm/assembler.h
+ *
+ * This file contains arm architecture specific defines
+ * for the different processors
+ */
+
+#if !defined (__arm2__) && !defined (__arm3__) && !defined (__arm6__)
+#error "Only ARN2, ARM3 and ARM6 architectures are supported"
+#endif
+
+#undef __FOUND
+#undef __ERROR
+
+#ifdef __arm2__
+#ifdef __FOUND
+#undef __ERROR
+#define __ERROR
+#else
+#define __FOUND
+#endif
+#endif
+
+#ifdef __arm3__
+#ifdef __FOUND
+#undef __ERROR
+#define __ERROR
+#else
+#define __FOUND
+#endif
+#endif
+
+#ifdef __arm6__
+#ifdef __FOUND
+#undef __ERROR
+#define __ERROR
+#else
+#define __FOUND
+#endif
+#endif
+
+#ifdef __ERROR
+#error "Only one of __arm3__ and __arm6__ may be defined"
+#endif
+
+#undef __FOUND
+#undef __ERROR
+
+/*
+ * LOADREGS: multiple register load (ldm) with pc in register list
+ *		(takes account of ARM6 not using ^)
+ *
+ * RETINSTR: return instruction: adds the 's' in at the end of the
+ *		instruction if this is not an ARM6
+ *
+ * SAVEIRQS: save IRQ state (not required on ARM2/ARM3 - done
+ *		implicitly
+ *
+ * RESTOREIRQS: restore IRQ state (not required on ARM2/ARM3 - done
+ *		implicitly with ldm ... ^ or movs.
+ *
+ * These next two need thinking about - can't easily use stack... (see system.S)
+ * DISABLEIRQS: disable IRQS in SVC mode
+ *
+ * ENABLEIRQS: enable IRQS in SVC mode
+ *
+ * USERMODE: switch to USER mode
+ *
+ * SVCMODE: switch to SVC mode
+ */
+
+#if defined (__arm2__)
+#define LOADREGS(cond, base, reglist...)\
+	ldm##cond	base,reglist^
+
+#define RETINSTR(instr, regs...)\
+	instr##s	regs
+
+#define MODENOP\
+	mov	r0, r0
+
+#define SAVEIRQS
+#define RESTOREIRQS
+
+#define DISABLEIRQS\
+	teqp	pc, #0x08000003
+
+#define ENABLEIRQS\
+	teqp	pc, #0x00000003
+
+#define USERMODE\
+	teqp	pc, #0x00000000;\
+	mov	r0, r0
+
+#define SVCMODE\
+	teqp	pc, #0x00000003;\
+	mov	r0, r0
+#endif
+
+#if defined (__arm3__)
+#define LOADREGS(cond, base, reglist...)\
+	ldm##cond	base,reglist^
+
+#define RETINSTR(instr, regs...)\
+	instr##s	regs
+
+#define MODENOP
+
+#define SAVEIRQS
+#define RESTOREIRQS
+
+#define DISABLEIRQS\
+	teqp	pc, #0x08000003
+
+#define ENABLEIRQS\
+	teqp	pc, #0x00000003
+
+#define USERMODE\
+	teqp	pc, #0x00000000
+
+#define SVCMODE\
+	teqp	pc, #0x00000003
+#endif
+
+#if defined (__arm6__)
+#define LOADREGS(cond, base, reglist...)\
+	ldm##cond	base,reglist
+
+#define RETINSTR(instr, regs...)\
+	instr	regs
+
+#define MODENOP
+
+#define SAVEIRQS
+#define RESTOREIRQS
+
+#define DISABLEIRQS
+#define ENABLEIRQS
+#define USERMODE
+#define SVCMODE
+#endif
diff -urNwbB linux/include/asm-arm/bitops.h linux.arm/include/asm-arm/bitops.h
--- linux/include/asm-arm/bitops.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/bitops.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,42 @@
+#ifndef __ASM_ARM_BITOPS_H
+#define __ASM_ARM_BITOPS_H
+
+/*
+ * Copyright 1995, Russell King
+ */
+
+/*
+ * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
+ */
+
+extern int set_bit(int nr, void * addr);
+extern int clear_bit(int nr, void * addr);
+extern int change_bit(int nr, void * addr);
+
+/*
+ * This routine doesn't need to be atomic, but it's faster to code it
+ * this way.
+ */
+extern int test_bit(int nr, void * addr);
+
+/*
+ * Find-bit routines..
+ */
+extern int find_first_zero_bit(void * addr, unsigned size);
+extern int find_next_zero_bit (void * addr, int size, int offset);
+
+/*
+ * ffz = Find First Zero in word. Undefined if no zero exists.
+ */
+extern inline unsigned long ffz(unsigned long word)
+{
+	int k;
+	
+	for (k = 0; k < 32; k++)
+		if ((word & (1<<k))==0)
+			break;
+        return k;
+}
+
+#endif /* _ARM_BITOPS_H */
+
diff -urNwbB linux/include/asm-arm/bugs.h linux.arm/include/asm-arm/bugs.h
--- linux/include/asm-arm/bugs.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/bugs.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,17 @@
+/*
+ *  include/asm-arm/bugs.h
+ *
+ *  Copyright (C) 1995  Russell King
+ */
+
+/*
+ * This is included by init/main.c to check for architecture-dependent bugs.
+ *
+ * Needs:
+ *      void check_bugs(void);
+ */
+         
+static inline void check_bugs(void)
+{
+}
+
diff -urNwbB linux/include/asm-arm/byteorder.h linux.arm/include/asm-arm/byteorder.h
--- linux/include/asm-arm/byteorder.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/byteorder.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,87 @@
+#ifndef __ASM_ARM_BYTEORDER_H
+#define __ASM_ARM_BYTEORDER_H
+
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#endif
+
+#ifndef __LITTLE_ENDIAN_BITFIELD
+#define __LITTLE_ENDIAN_BITFIELD
+#endif
+
+extern unsigned long int        ntohl(unsigned long int);
+extern unsigned short int       ntohs(unsigned short int);
+extern unsigned long int        htonl(unsigned long int);
+extern unsigned short int       htons(unsigned short int);
+
+extern unsigned long int        __ntohl(unsigned long int);
+extern unsigned short int       __ntohs(unsigned short int);
+extern unsigned long int        __constant_ntohl(unsigned long int);
+extern unsigned short int       __constant_ntohs(unsigned short int);
+
+extern __inline__ unsigned long int
+__ntohl(unsigned long int x)
+{
+	unsigned long xx;
+	__asm__("eor\t%1, %0, %0, ror #16\n\t"
+		"bic\t%1, %1, #0xff0000\n\t"
+		"mov\t%0, %0, ror #8\n\t"
+		"eor\t%0, %0, %1, lsr #8\n\t"
+		: "=r" (x), "=r" (xx)
+		: "0" (x));
+	return x;
+}
+
+#define __constant_ntohl(x) \
+	((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
+			     (((unsigned long int)(x) & 0x0000ff00U) <<  8) | \
+			     (((unsigned long int)(x) & 0x00ff0000U) >>  8) | \
+			     (((unsigned long int)(x) & 0xff000000U) >> 24)))
+
+extern __inline__ unsigned short int
+__ntohs(unsigned short int x)
+{
+	__asm__("eor\t%0, %0, %0, lsr #8\n\t"
+		"eor\t%0, %0, %0, lsl #8\n\t"
+		"bic\t%0, %0, #0xff0000\n\t"
+		"eor\t%0, %0, %0, lsr #8\n\t"
+		: "=r" (x) 
+		: "0" (x));
+	return x;
+}
+
+#define __constant_ntohs(x) \
+	((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
+			      (((unsigned short int)(x) & 0xff00) >> 8)))
+
+#define __htonl(x) __ntohl(x)
+#define __htons(x) __ntohs(x)
+#define __constant_htonl(x) __constant_ntohl(x)
+#define __constant_htons(x) __constant_ntohs(x)
+
+#ifdef  __OPTIMIZE__
+#  define ntohl(x) \
+(__builtin_constant_p((long)(x)) ? \
+ __constant_ntohl((x)) : \
+ __ntohl((x)))
+#  define ntohs(x) \
+(__builtin_constant_p((short)(x)) ? \
+ __constant_ntohs((x)) : \
+ __ntohs((x)))
+#  define htonl(x) \
+(__builtin_constant_p((long)(x)) ? \
+ __constant_htonl((x)) : \
+ __htonl((x)))
+#  define htons(x) \
+(__builtin_constant_p((short)(x)) ? \
+ __constant_htons((x)) : \
+ __htons((x)))
+#endif
+
+#endif
+
diff -urNwbB linux/include/asm-arm/checksum.h linux.arm/include/asm-arm/checksum.h
--- linux/include/asm-arm/checksum.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/checksum.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,142 @@
+#ifndef __ASM_ARM_CHECKSUM_H
+#define __ASM_ARM_CHECKSUM_H
+
+#ifndef __ASM_ARM_SEGMENT_H
+#include <asm/segment.h>
+#endif
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum);
+
+
+/*
+ * the same as csum_partial_copy, but copies from user space.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+static __INLINE__ unsigned int csum_partial_copy_fromuser(const char *src, char *dst, int len, int sum)
+{
+	extern unsigned int __csum_partial_copy_fromuser(const char *src, char *dst, int len, int sum);
+
+	if (IS_USER_SEG)
+		return __csum_partial_copy_fromuser (src, dst, len, sum);
+	else
+		return csum_partial_copy (src, dst, len, sum);
+}
+
+/*
+ *	This is a version of ip_compute_csum() optimized for IP headers,
+ *	which always checksum on 4 octet boundaries.
+ *
+ *	By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
+ *	Arnt Gulbrandsen.
+ *
+ *	Converted to ARM by R.M.King
+ */
+static inline unsigned short ip_fast_csum(unsigned char * iph,
+					  unsigned int ihl) {
+	unsigned int sum, tmp;
+
+    __asm__ __volatile__("
+    mov		%0, #0
+    adds	%0, %0, #0
+1:  ldr		%3, [%1], #4
+    adcs	%0, %0, %3
+    sub		%2, %2, #1
+    teq		%2, #0
+    bne		1b
+    adc		%0, %0, #0
+    adds	%0, %0, %0, lsl #16
+    addcs	%0, %0, #0x10000
+    mvn		%0, %0
+    mov		%0, %0, lsr #16
+        "
+	: "=&r" (sum), "=&r" (iph), "=&r" (ihl), "=&r" (tmp)
+	: "1" (iph), "2" (ihl));
+	return(sum);
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
+						   unsigned long daddr,
+						   unsigned short len,
+						   unsigned short proto,
+						   unsigned int sum) {
+    __asm__ __volatile__("
+    adds	%0, %0, %1
+    adcs	%0, %0, %4
+    adcs	%0, %0, %5
+    adc		%0, %0, #0
+    adds	%0, %0, %0, lsl #16
+    addcs	%0, %0, #0x10000
+    mvn		%0, %0
+    mov		%0, %0, lsr #16
+	"
+	: "=&r" (sum), "=&r" (saddr)
+	: "0" (daddr), "1"(saddr), "r"((ntohs(len)<<16)+proto*256), "r"(sum));
+	return((unsigned short)sum);
+}
+
+/*
+ * 	Fold a partial checksum without adding pseudo headers
+ */
+static inline unsigned int csum_fold(unsigned int sum)
+{
+    __asm__ __volatile__("
+    adds	%0, %0, %0, lsl #16
+    addcs	%0, %0, #0x10000
+    mvn		%0, %0
+    mov		%0, %0, lsr #16
+	"
+	: "=r" (sum)
+	: "0" (sum));
+	return sum;
+}	
+	
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+
+static inline unsigned short ip_compute_csum(unsigned char * buff, int len) {
+    unsigned int sum;
+
+    __asm__ __volatile__("
+    adds	%0, %0, %0, lsl #16
+    addcs	%0, %0, #0x10000
+    mvn		%0, %0
+    mov		%0, %0, lsr #16
+	"
+	: "=r"(sum)
+	: "0" (csum_partial(buff, len, 0)));
+	return(sum);
+}
+
+#endif
diff -urNwbB linux/include/asm-arm/delay.h linux.arm/include/asm-arm/delay.h
--- linux/include/asm-arm/delay.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/delay.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,27 @@
+#ifndef __ASM_ARM_DELAY_H
+#define __ASM_ARM_DELAY_H
+
+/*
+ * Copyright (C) 1995 Russell King
+ *
+ * Delay routines, using a pre-computed "loops_per_second" value.
+ */
+
+extern void __delay(int loops);
+
+/*
+ * division by multiplication: you don't have to worry about
+ * loss of precision.
+ *
+ * Use only for very small delays ( < 1 msec).  Should probably use a
+ * lookup table, really, as the multiplications take much too long with
+ * short delays.  This is a "reasonable" implementation, though (and the
+ * first constant multiplications gets optimized away if the delay is
+ * a constant)
+ */
+extern void udelay(unsigned long usecs);
+
+extern unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c);
+
+#endif /* defined(_ARM_DELAY_H) */
+
diff -urNwbB linux/include/asm-arm/dma.h linux.arm/include/asm-arm/dma.h
--- linux/include/asm-arm/dma.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/dma.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,77 @@
+#ifndef __ASM_ARM_DMA_H
+#define __ASM_ARM_DMA_H
+
+#include <asm/irq.h>
+
+#define MAX_DMA_CHANNELS	8
+
+#define MAX_DMA_ADDRESS		0x03000000
+
+#define DMA_MODE_READ		0x44
+#define DMA_MODE_WRITE		0x48
+
+/* enable/disable a specific DMA channel */
+extern void enable_dma(unsigned int dmanr);
+
+static __inline__ void disable_dma(unsigned int dmanr)
+{
+    switch(dmanr) {
+	case 2:  disable_irq(16); break;
+	default: printk("disable_dma: dma %d not supported\n", dmanr); break;
+    }
+}
+
+/* Clear the 'DMA Pointer Flip Flop'.
+ * Write 0 for LSB/MSB, 1 for MSB/LSB access.
+ * Use this once to initialize the FF to a known state.
+ * After that, keep track of it. :-)
+ * --- In order to do that, the DMA routines below should ---
+ * --- only be used while interrupts are disabled! ---
+ */
+static __inline__ void clear_dma_ff(unsigned int dmanr)
+{
+    switch(dmanr) {
+	case 2:  break;
+	default: printk("clear_dma_ff: dma %d not supported\n", dmanr); break;
+    }
+}
+
+/* set mode (above) for a specific DMA channel */
+extern void set_dma_mode(unsigned int dmanr, char mode);
+
+/* Set only the page register bits of the transfer address.
+ * This is used for successive transfers when we know the contents of
+ * the lower 16 bits of the DMA current address register, but a 64k boundary
+ * may have been crossed.
+ */
+static __inline__ void set_dma_page(unsigned int dmanr, char pagenr)
+{
+    printk("set_dma_page: dma %d not supported\n", dmanr);
+}
+
+
+/* Set transfer address & page bits for specific DMA channel.
+ * Assumes dma flipflop is clear.
+ */
+extern void set_dma_addr(unsigned int dmanr, unsigned int addr);
+
+/* Set transfer size for a specific DMA channel.
+ */
+extern void set_dma_count(unsigned int dmanr, unsigned int count);
+
+/* Get DMA residue count. After a DMA transfer, this
+ * should return zero. Reading this while a DMA transfer is
+ * still in progress will return unpredictable results.
+ * If called before the channel has been used, it may return 1.
+ * Otherwise, it returns the number of _bytes_ left to transfer.
+ *
+ * Assumes DMA flip-flop is clear.
+ */
+extern int get_dma_residue(unsigned int dmanr);
+
+/* These are in kernel/dma.c: */
+extern int request_dma(unsigned int dmanr, const char * device_id);   /* reserve a DMA channel */
+extern void free_dma(unsigned int dmanr);       /* release it again */
+
+#endif /* _ARM_DMA_H */
+
diff -urNwbB linux/include/asm-arm/ecard.h linux.arm/include/asm-arm/ecard.h
--- linux/include/asm-arm/ecard.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/ecard.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,99 @@
+/*
+ * linux/include/asm-arm/ecard.h
+ *
+ * definitions for expansion cards
+ *
+ * This is a new system as from Linux 1.2.3
+ *
+ * Reference: Acorns Risc OS 3 Programmers Reference Manuals.
+ *
+ * Do not use the r_ fields - use the #defined ones
+ */
+
+/*
+ * Expansion card header
+ */
+
+struct ex_ecld
+{
+	unsigned char  r_ecld;
+	unsigned char  r_reserved[2];
+	unsigned char  r_product[2];
+	unsigned char  r_manufacturer[2];
+	unsigned char  r_country;
+	         long  r_fiqs;
+	         long  r_irqs;
+#define e_ecld(x)	((x)->r_ecld)
+#define e_cd(x)		((x)->r_reserved[0] & 1)
+#define e_is(x)		((x)->r_reserved[0] & 2)
+#define e_w(x)		(((x)->r_reserved[0] & 12)>>2)
+#define e_prod(x)	((x)->r_product[0]|((x)->r_product[1]<<8))
+#define e_manu(x)	((x)->r_manufacturer[0]|((x)->r_manufacturer[1]<<8))
+#define e_country(x)	((x)->r_country)
+#define e_fiqmask(x)	((x)->r_fiqs & 0xff)
+#define e_fiqaddr(x)	((x)->r_fiqs >> 8)
+#define e_irqmask(x)	((x)->r_irqs & 0xff)
+#define e_irqaddr(x)	((x)->r_irqs >> 8)
+};
+
+/*
+ * Chunk directory entry
+ */
+
+struct chunk_dir
+{
+	unsigned char r_id;
+	unsigned char r_len[3];
+	unsigned long r_start;
+	union
+	{
+		char string[256];
+		char data[1];
+	} d;
+#define c_id(x)		((x)->r_id)
+#define c_len(x)	((x)->r_len[0]|((x)->r_len[1]<<8)|((x)->r_len[2]<<16))
+#define c_start(x)	((x)->r_start)
+};
+
+/*
+ * This contains all the info needed on an expansion card
+ */
+
+struct expansion_card
+{
+	struct ex_ecld ecld;
+	unsigned char irq;
+	unsigned char fiq;
+	unsigned char unused1;
+	unsigned char unused2;
+	unsigned char *r_loader; /* loader program */
+	volatile unsigned char *r_podaddr;
+	volatile unsigned char *r_irqaddr;
+	volatile unsigned char *r_fiqaddr;
+	unsigned char r_irqmask;
+	unsigned char r_fiqmask;
+	unsigned char r_found;
+	unsigned char r_claimed;
+};
+
+/*
+ * functions exported
+ */
+
+#define ecard_claim(ec) ((ec)->r_claimed = 1)
+
+#define ecard_release(ec) ((ec)->r_claimed = 0)
+ 
+/*
+ * Find an expansion card with the correct cld, product and manufacturer code
+ */
+extern struct expansion_card *ecard_find(int cld, int num, int prod[], int manu[]);
+ 
+/*
+ * Read a chunk from an expansion card
+ * cd : where to put read data
+ * ec : expansion card info struct
+ * id : id number to find
+ * num: (n+1)'th id to find.
+ */
+extern int ecard_readchunk(struct chunk_dir *cd, struct expansion_card *ec, int id, int num);
diff -urNwbB linux/include/asm-arm/errno.h linux.arm/include/asm-arm/errno.h
--- linux/include/asm-arm/errno.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/errno.h	Sun Feb 11 09:32:54 1996
@@ -0,0 +1,127 @@
+#ifndef _ARM_ERRNO_H
+#define _ARM_ERRNO_H
+
+#define	EPERM		 1	/* Operation not permitted */
+#define	ENOENT		 2	/* No such file or directory */
+#define	ESRCH		 3	/* No such process */
+#define	EINTR		 4	/* Interrupted system call */
+#define	EIO		 5	/* I/O error */
+#define	ENXIO		 6	/* No such device or address */
+#define	E2BIG		 7	/* Arg list too long */
+#define	ENOEXEC		 8	/* Exec format error */
+#define	EBADF		 9	/* Bad file number */
+#define	ECHILD		10	/* No child processes */
+#define	EAGAIN		11	/* Try again */
+#define	ENOMEM		12	/* Out of memory */
+#define	EACCES		13	/* Permission denied */
+#define	EFAULT		14	/* Bad address */
+#define	ENOTBLK		15	/* Block device required */
+#define	EBUSY		16	/* Device or resource busy */
+#define	EEXIST		17	/* File exists */
+#define	EXDEV		18	/* Cross-device link */
+#define	ENODEV		19	/* No such device */
+#define	ENOTDIR		20	/* Not a directory */
+#define	EISDIR		21	/* Is a directory */
+#define	EINVAL		22	/* Invalid argument */
+#define	ENFILE		23	/* File table overflow */
+#define	EMFILE		24	/* Too many open files */
+#define	ENOTTY		25	/* Not a typewriter */
+#define	ETXTBSY		26	/* Text file busy */
+#define	EFBIG		27	/* File too large */
+#define	ENOSPC		28	/* No space left on device */
+#define	ESPIPE		29	/* Illegal seek */
+#define	EROFS		30	/* Read-only file system */
+#define	EMLINK		31	/* Too many links */
+#define	EPIPE		32	/* Broken pipe */
+#define	EDOM		33	/* Math argument out of domain of func */
+#define	ERANGE		34	/* Math result not representable */
+#define	EDEADLK		35	/* Resource deadlock would occur */
+#define	ENAMETOOLONG	36	/* File name too long */
+#define	ENOLCK		37	/* No record locks available */
+#define	ENOSYS		38	/* Function not implemented */
+#define	ENOTEMPTY	39	/* Directory not empty */
+#define	ELOOP		40	/* Too many symbolic links encountered */
+#define	EWOULDBLOCK	EAGAIN	/* Operation would block */
+#define	ENOMSG		42	/* No message of desired type */
+#define	EIDRM		43	/* Identifier removed */
+#define	ECHRNG		44	/* Channel number out of range */
+#define	EL2NSYNC	45	/* Level 2 not synchronized */
+#define	EL3HLT		46	/* Level 3 halted */
+#define	EL3RST		47	/* Level 3 reset */
+#define	ELNRNG		48	/* Link number out of range */
+#define	EUNATCH		49	/* Protocol driver not attached */
+#define	ENOCSI		50	/* No CSI structure available */
+#define	EL2HLT		51	/* Level 2 halted */
+#define	EBADE		52	/* Invalid exchange */
+#define	EBADR		53	/* Invalid request descriptor */
+#define	EXFULL		54	/* Exchange full */
+#define	ENOANO		55	/* No anode */
+#define	EBADRQC		56	/* Invalid request code */
+#define	EBADSLT		57	/* Invalid slot */
+#define	EDEADLOCK	58	/* File locking deadlock error */
+#define	EBFONT		59	/* Bad font file format */
+#define	ENOSTR		60	/* Device not a stream */
+#define	ENODATA		61	/* No data available */
+#define	ETIME		62	/* Timer expired */
+#define	ENOSR		63	/* Out of streams resources */
+#define	ENONET		64	/* Machine is not on the network */
+#define	ENOPKG		65	/* Package not installed */
+#define	EREMOTE		66	/* Object is remote */
+#define	ENOLINK		67	/* Link has been severed */
+#define	EADV		68	/* Advertise error */
+#define	ESRMNT		69	/* Srmount error */
+#define	ECOMM		70	/* Communication error on send */
+#define	EPROTO		71	/* Protocol error */
+#define	EMULTIHOP	72	/* Multihop attempted */
+#define	EDOTDOT		73	/* RFS specific error */
+#define	EBADMSG		74	/* Not a data message */
+#define	EOVERFLOW	75	/* Value too large for defined data type */
+#define	ENOTUNIQ	76	/* Name not unique on network */
+#define	EBADFD		77	/* File descriptor in bad state */
+#define	EREMCHG		78	/* Remote address changed */
+#define	ELIBACC		79	/* Can not access a needed shared library */
+#define	ELIBBAD		80	/* Accessing a corrupted shared library */
+#define	ELIBSCN		81	/* .lib section in a.out corrupted */
+#define	ELIBMAX		82	/* Attempting to link in too many shared libraries */
+#define	ELIBEXEC	83	/* Cannot exec a shared library directly */
+#define	EILSEQ		84	/* Illegal byte sequence */
+#define	ERESTART	85	/* Interrupted system call should be restarted */
+#define	ESTRPIPE	86	/* Streams pipe error */
+#define	EUSERS		87	/* Too many users */
+#define	ENOTSOCK	88	/* Socket operation on non-socket */
+#define	EDESTADDRREQ	89	/* Destination address required */
+#define	EMSGSIZE	90	/* Message too long */
+#define	EPROTOTYPE	91	/* Protocol wrong type for socket */
+#define	ENOPROTOOPT	92	/* Protocol not available */
+#define	EPROTONOSUPPORT	93	/* Protocol not supported */
+#define	ESOCKTNOSUPPORT	94	/* Socket type not supported */
+#define	EOPNOTSUPP	95	/* Operation not supported on transport endpoint */
+#define	EPFNOSUPPORT	96	/* Protocol family not supported */
+#define	EAFNOSUPPORT	97	/* Address family not supported by protocol */
+#define	EADDRINUSE	98	/* Address already in use */
+#define	EADDRNOTAVAIL	99	/* Cannot assign requested address */
+#define	ENETDOWN	100	/* Network is down */
+#define	ENETUNREACH	101	/* Network is unreachable */
+#define	ENETRESET	102	/* Network dropped connection because of reset */
+#define	ECONNABORTED	103	/* Software caused connection abort */
+#define	ECONNRESET	104	/* Connection reset by peer */
+#define	ENOBUFS		105	/* No buffer space available */
+#define	EISCONN		106	/* Transport endpoint is already connected */
+#define	ENOTCONN	107	/* Transport endpoint is not connected */
+#define	ESHUTDOWN	108	/* Cannot send after transport endpoint shutdown */
+#define	ETOOMANYREFS	109	/* Too many references: cannot splice */
+#define	ETIMEDOUT	110	/* Connection timed out */
+#define	ECONNREFUSED	111	/* Connection refused */
+#define	EHOSTDOWN	112	/* Host is down */
+#define	EHOSTUNREACH	113	/* No route to host */
+#define	EALREADY	114	/* Operation already in progress */
+#define	EINPROGRESS	115	/* Operation now in progress */
+#define	ESTALE		116	/* Stale NFS file handle */
+#define	EUCLEAN		117	/* Structure needs cleaning */
+#define	ENOTNAM		118	/* Not a XENIX named type file */
+#define	ENAVAIL		119	/* No XENIX semaphores available */
+#define	EISNAM		120	/* Is a named type file */
+#define	EREMOTEIO	121	/* Remote I/O error */
+#define	EDQUOT		122	/* Quota exceeded */
+
+#endif
diff -urNwbB linux/include/asm-arm/fcntl.h linux.arm/include/asm-arm/fcntl.h
--- linux/include/asm-arm/fcntl.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/fcntl.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,64 @@
+#ifndef _ARM_FCNTL_H
+#define _ARM_FCNTL_H
+
+/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
+   located on an ext2 file system */
+#define O_ACCMODE	  0003
+#define O_RDONLY	    00
+#define O_WRONLY	    01
+#define O_RDWR		    02
+#define O_CREAT		  0100	/* not fcntl */
+#define O_EXCL		  0200	/* not fcntl */
+#define O_NOCTTY	  0400	/* not fcntl */
+#define O_TRUNC		 01000	/* not fcntl */
+#define O_APPEND	 02000
+#define O_NONBLOCK	 04000
+#define O_NDELAY	O_NONBLOCK
+#define O_SYNC		010000
+#define FASYNC		020000	/* fcntl, for BSD compatibility */
+
+#define F_DUPFD		0	/* dup */
+#define F_GETFD		1	/* get f_flags */
+#define F_SETFD		2	/* set f_flags */
+#define F_GETFL		3	/* more flags (cloexec) */
+#define F_SETFL		4
+#define F_GETLK		5
+#define F_SETLK		6
+#define F_SETLKW	7
+
+#define F_SETOWN	8	/*  for sockets. */
+#define F_GETOWN	9	/*  for sockets. */
+
+/* for F_[GET|SET]FL */
+#define FD_CLOEXEC	1	/* actually anything with low bit set goes */
+
+/* for posix fcntl() and lockf() */
+#define F_RDLCK		0
+#define F_WRLCK		1
+#define F_UNLCK		2
+
+/* for old implementation of bsd flock () */
+#define F_EXLCK		4	/* or 3 */
+#define F_SHLCK		8	/* or 4 */
+
+/* operations for bsd flock(), also used by the kernel implementation */
+#define LOCK_SH		1	/* shared lock */
+#define LOCK_EX		2	/* exclusive lock */
+#define LOCK_NB		4	/* or'd with one of the above to prevent
+				   blocking */
+#define LOCK_UN		8	/* remove lock */
+
+#ifdef __KERNEL__
+#define F_POSIX		1
+#define F_FLOCK		2
+#endif /* __KERNEL__ */
+
+struct flock {
+	short l_type;
+	short l_whence;
+	off_t l_start;
+	off_t l_len;
+	pid_t l_pid;
+};
+
+#endif
diff -urNwbB linux/include/asm-arm/io.h linux.arm/include/asm-arm/io.h
--- linux/include/asm-arm/io.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/io.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,189 @@
+#ifndef __ASM_ARM_IO_H
+#define __ASM_ARM_IO_H
+
+/*
+ * Change virtual address to physical addresses
+ */
+extern inline unsigned long virt_to_phys(volatile void * address)
+{
+	return (unsigned long) address;
+}
+
+extern inline void * phys_to_virt(unsigned long address)
+{
+	return (void *) address;
+}
+
+extern unsigned long io_base;
+
+/*
+ * IO -> physical
+ */
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+
+#undef USE_INLINE
+
+#ifdef USE_INLINE
+extern inline void __outb(unsigned char value, unsigned int port)		\
+{										\
+	__asm__ __volatile__(							\
+	"cmp\t%2, #0x00c00000\n\t"						\
+	"movge\t%0, #0\n\t"							\
+	"movlt\t%0, #0x03000000\n\t"						\
+	"orrlt\t%0, %0, #0x00010000\n\t"					\
+	"strb\t%1, [%0, %2, lsl #2\n\t"	:					\
+	"=r" (unused) : "r" (ret), "r" (port));				\
+}
+
+extern inline void __inb(unsigned int port)					\
+{										\
+	unsigned char ret, unused;						\
+	__asm__ __volatile__(							\
+	"cmp\t%1, #0x00c00000\n\t"						\
+	"movge\t%2, #0\n\t"							\
+	"movlt\t%2, #0x03000000\n\t"						\
+	"orrlt\t%2, %2, #0x00010000\n\t"					\
+	"ldrb\t%0, [%2, %1, lsl #2\n\t"	:					\
+	"=r" (ret), "=r" (unused) : "r" (port));				\
+	return ret;								\
+}
+	
+#define __OUT1(s, x) \
+extern inline void __out##s(unsigned x value, unsigned int port)		\
+{										\
+	if(port < 0x00C00000)							\
+		*(unsigned x *)(0x03010000+(port << 2)) = value;		\
+	else									\
+		*(unsigned x *)(port << 2) = value;				\
+}
+
+#define __IN1(s, x) \
+extern inline unsigned int __in##s(unsigned int port)				\
+{										\
+	if(port < 0x00C00000)							\
+		return *(unsigned x *)(0x03010000+(port << 2));			\
+	else									\
+		return *(unsigned x *)(port << 2);				\
+}
+#else
+
+#define __OUT1(s, x) \
+extern void __out##s(unsigned x value, unsigned int port);
+#define __IN1(s, x) \
+extern unsigned x __in##s(unsigned int port);
+#endif
+
+#define __OUT(s, x)  \
+  __OUT1(s     , x); \
+  __OUT1(s##c  , x); \
+  __OUT1(s##_p , x); \
+  __OUT1(s##c_p, x);
+
+__OUT(b, char);
+__OUT(w, short);
+__OUT(l, long);
+
+#define __IN(s, x)  \
+  __IN1(s     , x); \
+  __IN1(s##c  , x); \
+  __IN1(s##_p , x); \
+  __IN1(s##c_p, x);
+
+__IN(b, char);
+__IN(w, short);
+__IN(l, long);
+
+#ifdef USE_INLINE
+#define __INS1(s, x) \
+extern inline void ins##s(unsigned int port, void *to, int len)			\
+{										\
+	unsigned x *p = (unsigned x *)to;					\
+	if(port < 0x00C00000)							\
+		port = 0x03010000+(port << 2);					\
+	else									\
+		port <<= 2;							\
+	while(len-- > 0)							\
+		*p++ = *(unsigned x *)port;					\
+}
+
+#define __OUTS1(s, x) \
+extern inline void outs##s(unsigned int port, const void *from, int len)	\
+{										\
+	unsigned x *p = (unsigned x *)from;					\
+	if(port < 0x00C00000)							\
+		port = 0x03010000+(port << 2);					\
+	else									\
+		port <<= 2;							\
+	while(len-- > 0)							\
+		*(unsigned x *)port = *p++;					\
+}
+
+#else
+
+#define __INS1(s, x) \
+extern void ins##s(unsigned int port, void *to, int len);
+
+#define __OUTS1(s, x) \
+extern void outs##s(unsigned int port, const void *from, int len);
+
+#endif
+
+#define __INS(s, x)	\
+  __INS1(s     , x);	\
+  __INS1(s##c  , x);	\
+  __INS1(s##_p , x);	\
+  __INS1(s##c_p, x);
+
+__INS(b, char);
+__INS(w, short);
+__INS(l, long);
+
+#define __OUTS(s, x)	\
+  __OUTS1(s     , x);	\
+  __OUTS1(s##c  , x);	\
+  __OUTS1(s##_p , x);	\
+  __OUTS1(s##c_p, x);
+
+__OUTS(b, char);
+__OUTS(w, short);
+__OUTS(l, long);
+
+#define outb(val, port)	\
+  (__builtin_constant_p((port)) ? __outbc((val),(port)) : __outb((val),(port)))
+
+#define outb_p(val, port)	\
+  (__builtin_constant_p((port)) ? __outbc_p((val),(port)) : __outb_p((val),(port)))
+
+#define outw(val, port)	\
+  (__builtin_constant_p((port)) ? __outwc((val),(port)) : __outw((val),(port)))
+
+#define outw_p(val, port)	\
+  (__builtin_constant_p((port)) ? __outwc_p((val),(port)) : __outw_p((val),(port)))
+  
+#define outl(val, port)	\
+  (__builtin_constant_p((port)) ? __outlc((val),(port)) : __outl((val),(port)))
+
+#define outl_p(val, port)	\
+  (__builtin_constant_p((port)) ? __outlc_p((val),(port)) : __outl_p((val),(port)))
+
+#define inb(port)	\
+  (__builtin_constant_p((port)) ? __inbc((port)) : __inb((port)))
+
+#define inb_p(port)	\
+  (__builtin_constant_p((port)) ? __inbc_p((port)) : __inb_p((port)))
+
+#define inw(port)	\
+  (__builtin_constant_p((port)) ? __inwc((port)) : __inw((port)))
+
+#define inw_p(port)	\
+  (__builtin_constant_p((port)) ? __inwc_p((port)) : __inw_p((port)))
+  
+#define inl(port)	\
+  (__builtin_constant_p((port)) ? __inlc((port)) : __inl((port)))
+
+#define inl_p(port)	\
+  (__builtin_constant_p((port)) ? __inlc_p((port)) : __inl_p((port)))
+
+#endif
+
diff -urNwbB linux/include/asm-arm/ioctl.h linux.arm/include/asm-arm/ioctl.h
--- linux/include/asm-arm/ioctl.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/ioctl.h	Sat Feb 24 09:37:24 1996
@@ -0,0 +1,89 @@
+/* $Id: ioctl.h,v 1.5 1993/07/19 21:53:50 root Exp root $
+ *
+ * linux/ioctl.h for Linux by H.H. Bergman.
+ */
+
+#ifndef _ASMI386_IOCTL_H
+#define _ASMI386_IOCTL_H
+
+#include <asm/page.h>		/* for PAGE_SIZE */
+
+/* ioctl command encoding: 32 bits total, command in lower 16 bits,
+ * size of the parameter structure in the lower 14 bits of the
+ * upper 16 bits.
+ * Encoding the size of the parameter structure in the ioctl request
+ * is useful for catching programs compiled with old versions
+ * and to avoid overwriting user space outside the user buffer area.
+ * The highest 2 bits are reserved for indicating the ``access mode''.
+ * NOTE: This limits the max parameter size to 16kB -1 !
+ */
+
+#define IOC_VOID	0x00000000	/* param in size field */
+#define IOC_IN		0x40000000	/* user --> kernel */
+#define IOC_OUT		0x80000000	/* kernel --> user */
+#define IOC_INOUT	(IOC_IN | IOC_OUT)	/* both */
+#define IOCSIZE_MASK	0x3fff0000	/* size (max 16k-1 bytes) */
+#define IOCSIZE_SHIFT	16		/* how to get the size */
+#define IOCSIZE_MAX	((PAGE_SIZE-1)&(IOCSIZE_MASK >> IOCSIZE_SHIFT))
+#define IOCCMD_MASK	0x0000ffff	/* command code */
+#define IOCCMD_SHIFT	0
+#define IOCPARM_MASK IOCCMD_MASK
+#define IOCPARM_SHIFT IOCCMD_SHIFT
+
+#define IOC_SIZE(cmd)	(((cmd) & IOCSIZE_MASK) >> IOCSIZE_SHIFT)
+#define IOCBASECMD(cmd)	((cmd) & ~IOCPARM_MASK)
+#define IOCGROUP(cmd)	(((cmd) >> 8) & 0xFF)
+
+/* _IO(magic, subcode); size field is zero and the 
+ * subcode determines the command.
+ */
+#define _IO(c,d)	(IOC_VOID | ((c)<<8) | (d)) /* param encoded */
+
+/* _IOXX(magic, subcode, arg_t); where arg_t is the type of the
+ * (last) argument field in the ioctl call, if present.
+ */
+#define _IOW(c,d,t)	(IOC_IN | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
+				  ((c)<<8) | (d))
+#define _IOR(c,d,t)	(IOC_OUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
+				   ((c)<<8) | (d))
+/* WR rather than RW to avoid conflict with stdio.h */
+#define _IOWR(c,d,t)	(IOC_INOUT | ((sizeof(t)<<16) & IOCSIZE_MASK) | \
+				     ((c)<<8) | (d))
+
+/*
+ * The following is for compatibility across the various Linux
+ * platforms.  The i386 ioctl numbering scheme doesn't really enforce
+ * a type field.  De facto, however, the top 8 bits of the lower 16
+ * bits are indeed used as a type field, so we might just as well make
+ * this explicit here.  Please be sure to use the decoding macros
+ * below from now on.
+ */
+#define _IOC_NRBITS	8
+#define _IOC_TYPEBITS	8
+#define _IOC_SIZEBITS	14
+#define _IOC_DIRBITS	2
+
+#define _IOC_NRMASK	((1 << _IOC_NRBITS)-1)
+#define _IOC_TYPEMASK	((1 << _IOC_TYPEBITS)-1)
+#define _IOC_SIZEMASK	((1 << _IOC_SIZEBITS)-1)
+#define _IOC_DIRMASK	((1 << _IOC_DIRBITS)-1)
+
+#define _IOC_NRSHIFT	0
+#define _IOC_TYPESHIFT	(_IOC_NRSHIFT+_IOC_NRBITS)
+#define _IOC_SIZESHIFT	(_IOC_TYPESHIFT+_IOC_TYPEBITS)
+#define _IOC_DIRSHIFT	(_IOC_SIZESHIFT+_IOC_SIZEBITS)
+
+/*
+ * Direction bits.
+ */
+#define _IOC_NONE	0U
+#define _IOC_READ	1U
+#define _IOC_WRITE	2U
+
+/* used to decode ioctl numbers.. */
+#define _IOC_DIR(nr)		(((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
+#define _IOC_TYPE(nr)		(((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
+#define _IOC_NR(nr)		(((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
+#define _IOC_SIZE(nr)		(((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
+
+#endif /* _ASMI386_IOCTL_H */
diff -urNwbB linux/include/asm-arm/irq-no.h linux.arm/include/asm-arm/irq-no.h
--- linux/include/asm-arm/irq-no.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/irq-no.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,25 @@
+#define IRQ_PRINTERBUSY		0
+#define IRQ_SERIALRING		1
+#define IRQ_PRINTERACK		2
+#define IRQ_VSYNCPULSE		3
+#define IRQ_POWERON		4
+#define IRQ_TIMER0		5
+#define IRQ_TIMER1		6
+#define IRQ_7			7
+#define IRQ_8			8
+#define IRQ_SOUNDCHANGE		9
+#define IRQ_SERIALPORT		10
+#define IRQ_HARDDISK		11
+#define IRQ_FLOPPYDISK		12
+#define IRQ_EXPANSIONCARD	13
+#define IRQ_KEYBOARDTX		14
+#define IRQ_KEYBOARDRX		15
+
+#define FIQ_FLOPPYDATA		0
+#define FIQ_ECONET		2
+#define FIQ_EXPANSIONCARD	6
+
+#define TIMER_IRQ		IRQ_TIMER0
+
+#define NUM_IRQS		32
+
diff -urNwbB linux/include/asm-arm/irq.h linux.arm/include/asm-arm/irq.h
--- linux/include/asm-arm/irq.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/irq.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,16 @@
+#ifndef __ASM_ARM_IRQ_H
+#define __ASM_ARM_IRQ_H
+
+#include <linux/linkage.h>
+#include <asm/segment.h>
+
+#define NR_IRQS 32
+
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
+
+#define __STR(x) #x
+#define STR(x) __STR(x)
+
+#endif
+
diff -urNwbB linux/include/asm-arm/mman.h linux.arm/include/asm-arm/mman.h
--- linux/include/asm-arm/mman.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/mman.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,23 @@
+#ifndef __ARM_MMAN_H__
+#define __ARM_MMAN_H__
+
+#define PROT_READ	0x1		/* page can be read */
+#define PROT_WRITE	0x2		/* page can be written */
+#define PROT_EXEC	0x4		/* page can be executed */
+#define PROT_NONE	0x0		/* page can not be accessed */
+
+#define MAP_SHARED	0x01		/* Share changes */
+#define MAP_PRIVATE	0x02		/* Changes are private */
+#define MAP_TYPE	0x0f		/* Mask for type of mapping */
+#define MAP_FIXED	0x10		/* Interpret addr exactly */
+#define MAP_ANONYMOUS	0x20		/* don't use a file */
+
+#define MAP_GROWSDOWN	0x0100		/* stack-like segment */
+#define MAP_DENYWRITE	0x0800		/* ETXTBSY */
+#define MAP_EXECUTABLE	0x1000		/* mark it as a executable */
+
+#define MS_ASYNC	1		/* sync memory asynchronously */
+#define MS_INVALIDATE	2		/* invalidate the caches */
+#define MS_SYNC		4		/* synchronous memory sync */
+
+#endif /* __ARM_MMAN_H__ */
diff -urNwbB linux/include/asm-arm/page.h linux.arm/include/asm-arm/page.h
--- linux/include/asm-arm/page.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/page.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,109 @@
+#ifndef __ASM_ARM_PAGE_H
+#define __ASM_ARM_PAGE_H
+
+#include <linux/config.h>
+
+/* PAGE_SHIFT determines the page size */
+/* Check for 32K and 16K page sizes */
+#ifdef CONFIG_PAGE_32K
+#define PAGE_SHIFT	15
+#else
+#ifdef CONFIG_PAGE_16K
+#define PAGE_SHIFT	14
+#else
+#define PAGE_SHIFT	page_shift
+extern char page_shift;
+#endif
+#endif
+
+#define LOGICAL_END	0x02000000
+
+#define PAGE_SIZE       (1UL << PAGE_SHIFT)
+#define PAGE_MASK       (~(PAGE_SIZE-1))
+
+#ifdef __KERNEL__
+
+#define CONFIG_STRICT_MM_TYPECHECKS
+
+#ifdef CONFIG_STRICT_MM_TYPECHECKS
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { unsigned long pte; } pte_t;
+typedef struct { unsigned long pmd; } pmd_t;
+typedef struct { unsigned long pgd; } pgd_t;
+typedef struct { unsigned long pgprot; } pgprot_t;
+
+#define pte_val(x)      ((x).pte)
+#define pmd_val(x)      ((x).pmd)
+#define pgd_val(x)      ((x).pgd)
+#define pgprot_val(x)   ((x).pgprot)
+
+#define __pte(x)        ((pte_t) { (x) } )
+#define __pmd(x)        ((pmd_t) { (x) } )
+#define __pgd(x)        ((pgd_t) { (x) } )
+#define __pgprot(x)     ((pgprot_t) { (x) } )
+
+#else
+/*
+ * .. while these make it easier on the compiler
+ */
+typedef unsigned long pte_t;
+typedef unsigned long pmd_t;
+typedef unsigned long pgd_t;
+typedef unsigned long pgprot_t;
+
+#define pte_val(x)      (x)
+#define pmd_val(x)      (x)
+#define pgd_val(x)      (x)
+#define pgprot_val(x)   (x)
+
+#define __pte(x)        (x)
+#define __pmd(x)        (x)
+#define __pgd(x)        (x)
+#define __pgprot(x)     (x)
+
+#endif
+
+extern void remap_task (pgd_t *);
+
+/*
+ * TLB invalidation:
+ *
+ *  - invalidate() invalidates the current task TLBs
+ *  - invalidate_all() invalidates all processes TLBs
+ *  - invalidate_task(task) invalidates the specified tasks TLB's
+ *  - invalidate_page(task, vmaddr) invalidates one page
+ */
+#define invalidate() \
+	remap_task(current->mm->pgd)
+#define invalidate_all() invalidate()
+#define invalidate_task(task) \
+do { if ((task)->mm == current->mm) invalidate(); } while (0)
+#define invalidate_page(task,addr) \
+do { if ((task)->mm == current->mm) invalidate(); } while (0)
+
+/* Certain architectures need to do special things when pte's
+ * within a page table are directly modified.  Thus, the following
+ * hook is made available.
+ */
+#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
+
+/* This handles the memory map.. */
+#define PAGE_OFFSET		0x02000000
+#define MAP_NR(addr)		(((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT)
+
+typedef struct {
+	unsigned count:24,
+		 age:6,
+		 dirty:1,
+		 reserved:1;
+} mem_map_t;
+
+#endif /* __KERNEL__ */
+
+#endif /* _ARM_PAGE_H */
+
diff -urNwbB linux/include/asm-arm/param.h linux.arm/include/asm-arm/param.h
--- linux/include/asm-arm/param.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/param.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,21 @@
+#ifndef __ASM_ARM_PARAM_H
+#define __ASM_ARM_PARAM_H
+
+#ifndef HZ
+#define HZ 100
+#endif
+
+#define EXEC_PAGESIZE   32768
+
+#ifndef NGROUPS
+#define NGROUPS         32
+#endif
+
+#ifndef NOGROUP
+#define NOGROUP         (-1)
+#endif
+
+#define MAXHOSTNAMELEN  64      /* max length of hostname */
+
+#endif
+
diff -urNwbB linux/include/asm-arm/pgtable.h linux.arm/include/asm-arm/pgtable.h
--- linux/include/asm-arm/pgtable.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/pgtable.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,285 @@
+#ifndef __ASM_ARM_PGTABLE_H
+#define __ASM_ARM_PGTABLE_H
+
+/* PMD_SHIFT determines the size of the area a second-level page table can map */
+#define PMD_SHIFT       PAGE_SHIFT
+#define PMD_SIZE        (1UL << PMD_SHIFT)
+#define PMD_MASK        (~(PMD_SIZE-1))
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+#define PGDIR_SHIFT     PAGE_SHIFT
+#define PGDIR_SIZE      (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK      (~(PGDIR_SIZE-1))
+
+/*
+ * entries per page directory level: the i386 is two-level, so
+ * we don't really have any PMD directory physically.
+ */
+#define PTRS_PER_PTE    1
+#define PTRS_PER_PMD    1
+#define PTRS_PER_PGD    1024
+
+/* Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts.  That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_START	0x01A00000
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+
+#define _PAGE_PRESENT   0x001
+#define _PAGE_RW        0x002
+#define _PAGE_USER      0x004
+#define _PAGE_PCD       0x010
+#define _PAGE_ACCESSED  0x020
+#define _PAGE_DIRTY     0x040
+#define _PAGE_COW       0x200   /* implemented in software (one of the AVL bits) */
+
+#define _PAGE_TABLE     (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
+#define _PAGE_CHG_MASK  (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
+
+#define PAGE_NONE       __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
+#define PAGE_SHARED     __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
+#define PAGE_COPY       __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED | _PAGE_COW)
+#define PAGE_READONLY   __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
+#define PAGE_KERNEL     __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
+
+/*
+ * The i386 can't do page protection for execute, and considers that the same are read.
+ * Also, write permissions imply read permissions. This is the closest we can get..
+ */
+#define __P000  PAGE_NONE
+#define __P001  PAGE_READONLY
+#define __P010  PAGE_COPY
+#define __P011  PAGE_COPY
+#define __P100  PAGE_READONLY
+#define __P101  PAGE_READONLY
+#define __P110  PAGE_COPY
+#define __P111  PAGE_COPY
+
+#define __S000  PAGE_NONE
+#define __S001  PAGE_READONLY
+#define __S010  PAGE_SHARED
+#define __S011  PAGE_SHARED
+#define __S100  PAGE_READONLY
+#define __S101  PAGE_READONLY
+#define __S110  PAGE_SHARED
+#define __S111  PAGE_SHARED
+
+/*
+ * Define this if things work differently on a i386 and a i486:
+ * it will (on a i486) warn about kernel memory accesses that are
+ * done without a 'verify_area(VERIFY_WRITE,..)'
+ */
+#undef CONFIG_TEST_VERIFY_AREA
+
+#ifndef not_yet_checked
+/* page table for 0-4MB for everybody */
+extern unsigned long pg0[1024];
+#endif
+
+/*
+ * 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 pte_t __bad_page(void);
+extern unsigned long __zero_page(void);
+
+#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 align the pointer to a pointer address */
+#define PTR_MASK                        (~(sizeof(void*)-1))
+
+/* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */
+/* 64-bit machines, beware!  SRB. */
+#define SIZEOF_PTR_LOG2                 2
+
+/* to set the page-dir */
+#define SET_PAGE_DIR(tsk,pgdir) \
+do { \
+	if ((tsk) == current) \
+		remap_task((pgdir)); \
+} while (0)
+
+extern unsigned long physical_start;
+extern unsigned long physical_end;
+
+extern inline int pte_none(pte_t pte)           { return !pte_val(pte); }
+extern inline int pte_present(pte_t pte)        { return pte_val(pte) & _PAGE_PRESENT; }
+extern inline int pte_inuse(pte_t *ptep)        { return mem_map[MAP_NR(ptep)].reserved || mem_map[MAP_NR(ptep)].count != 1; }
+extern inline void pte_clear(pte_t *ptep)       { pte_val(*ptep) = 0; }
+extern inline void pte_reuse(pte_t * ptep)
+{
+	if (!mem_map[MAP_NR(ptep)].reserved)
+		mem_map[MAP_NR(ptep)].count++;
+}
+
+extern inline int pmd_none(pmd_t pmd)           { return 0; }
+extern inline int pmd_bad(pmd_t pmd)            { return 0; }
+extern inline int pmd_present(pmd_t pmd)        { return 1; }
+extern inline int pmd_inuse(pmd_t *pmdp)        { return 0; }
+extern inline void pmd_clear(pmd_t * pmdp)      { }
+extern inline void pmd_reuse(pmd_t * pmdp)      { }
+
+
+/*
+ * The "pgd_xxx()" functions here are trivial for a folded two-level
+ * setup: the pgd is never bad, and a pmd always exists (as it's folded
+ * into the pgd entry)
+ */
+extern inline int pgd_none(pgd_t pgd)           { return 0; }
+extern inline int pgd_bad(pgd_t pgd)            { return 0; }
+extern inline int pgd_present(pgd_t pgd)        { return 1; }
+extern inline int pgd_inuse(pgd_t * pgdp)       { return 0; }
+extern inline void pgd_clear(pgd_t * pgdp)      { }
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+extern inline int pte_read(pte_t pte)           { return pte_val(pte) & _PAGE_USER; }
+extern inline int pte_write(pte_t pte)          { return pte_val(pte) & _PAGE_RW; }
+extern inline int pte_exec(pte_t pte)           { return pte_val(pte) & _PAGE_USER; }
+extern inline int pte_dirty(pte_t pte)          { return pte_val(pte) & _PAGE_DIRTY; }
+extern inline int pte_young(pte_t pte)          { return pte_val(pte) & _PAGE_ACCESSED; }
+extern inline int pte_cow(pte_t pte)            { return pte_val(pte) & _PAGE_COW; }
+
+extern inline pte_t pte_wrprotect(pte_t pte)    { pte_val(pte) &= ~_PAGE_RW; return pte; }
+extern inline pte_t pte_rdprotect(pte_t pte)    { pte_val(pte) &= ~_PAGE_USER; return pte; }
+extern inline pte_t pte_exprotect(pte_t pte)    { pte_val(pte) &= ~_PAGE_USER; return pte; }
+extern inline pte_t pte_mkclean(pte_t pte)      { pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
+extern inline pte_t pte_mkold(pte_t pte)        { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
+extern inline pte_t pte_uncow(pte_t pte)        { pte_val(pte) &= ~_PAGE_COW; return pte; }
+extern inline pte_t pte_mkwrite(pte_t pte)      { pte_val(pte) |= _PAGE_RW; return pte; }
+extern inline pte_t pte_mkread(pte_t pte)       { pte_val(pte) |= _PAGE_USER; return pte; }
+extern inline pte_t pte_mkexec(pte_t pte)       { pte_val(pte) |= _PAGE_USER; return pte; }
+extern inline pte_t pte_mkdirty(pte_t pte)      { pte_val(pte) |= _PAGE_DIRTY; return pte; }
+extern inline pte_t pte_mkyoung(pte_t pte)      { pte_val(pte) |= _PAGE_ACCESSED; return pte; }
+extern inline pte_t pte_mkcow(pte_t pte)        { pte_val(pte) |= _PAGE_COW; return pte; }
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot)
+{ pte_t pte; pte_val(pte) = page | pgprot_val(pgprot); return pte; }
+
+extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
+
+extern inline unsigned long pte_page(pte_t pte)
+{ return pte_val(pte) & PAGE_MASK; }
+
+extern inline unsigned long pmd_page(pmd_t pmd)
+{ return pmd_val(pmd) & PAGE_MASK; }
+
+/* to find an entry in a page-table-directory */
+extern inline pgd_t * pgd_offset(struct mm_struct * mm, unsigned long address)
+{
+        return mm->pgd + (address >> PGDIR_SHIFT);
+}
+
+/* Find an entry in the second-level page table.. */
+extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
+{
+        return (pmd_t *) dir;
+}
+
+/* Find an entry in the third-level page table.. */
+extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address)
+{
+        return (pte_t *) dir;
+}
+
+/*
+ * Allocate and free page tables. The xxx_kernel() versions are
+ * used to allocate a kernel page table - this turns on ASN bits
+ * if any, and marks the page tables reserved.
+ */
+extern inline void pte_free_kernel(pte_t * pte)
+{
+	pte_val(*pte) = 0;
+}
+
+extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
+{
+	return (pte_t *) pmd;
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+extern inline void pmd_free_kernel(pmd_t * pmd)
+{
+}
+
+extern inline pmd_t * pmd_alloc_kernel(pgd_t * pgd, unsigned long address)
+{
+        return (pmd_t *) pgd;
+}
+
+extern inline void pte_free(pte_t * pte)
+{
+}
+
+extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
+{
+	return (pte_t *) pmd;
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+extern inline void pmd_free(pmd_t * pmd)
+{
+}
+
+extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
+{
+        return (pmd_t *) pgd;
+}
+
+extern inline void pgd_free(pgd_t * pgd)
+{
+	extern void kfree(void *);
+	kfree((void *)pgd);
+}
+
+extern inline pgd_t * pgd_alloc(void)
+{
+	pgd_t *pgd;
+	extern void *kmalloc(unsigned int, int);
+	
+        pgd = (pgd_t *) kmalloc(32*1024*1024*4/PAGE_SIZE, GFP_KERNEL);
+        memset(pgd, 0, 32*1024*1024*4/PAGE_SIZE);
+
+        return pgd;
+}
+
+extern pgd_t swapper_pg_dir[1024];
+
+/*
+ * The i386 doesn't have any external MMU info: the kernel page
+ * tables contain all the necessary information.
+ */
+extern inline void update_mmu_cache(struct vm_area_struct * vma,
+        unsigned long address, pte_t pte)
+{
+}
+
+#define SWP_TYPE(entry) (((entry) >> 1) & 0x7f)
+#define SWP_OFFSET(entry) ((entry) >> 8)
+#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) <<  8))
+
+#endif /* _ARM_PAGE_H */
+
diff -urNwbB linux/include/asm-arm/processor.h linux.arm/include/asm-arm/processor.h
--- linux/include/asm-arm/processor.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/processor.h	Sun Feb 11 09:32:55 1996
@@ -0,0 +1,76 @@
+/*
+ * include/asm-arm/processor.h
+ *
+ * Copyright (C) 1995 Russell King
+ */
+
+#ifndef __ASM_ARM_PROCESSOR_H
+#define __ASM_ARM_PROCESSOR_H
+
+/*
+ * Bus types
+ */
+#define EISA_bus 0
+#define EISA_bus__is_a_macro /* for versions in ksyms.c */
+#define MCA_bus 0
+#define MCA_bus__is_a_macro /* for versions in ksyms.c */
+
+#define ARM_bus	1
+#define ARM_bus__is_a_macro
+
+/*
+ * on arm2,3 wp does not work ok
+ */
+#define wp_works_ok 0
+#define wp_works_ok__is_a_macro /* for versions in ksyms.c */
+
+/*
+ * User space process size: 3GB. This is hardcoded into a few places,
+ * so don't change it unless you know what you are doing.
+ */
+#define TASK_SIZE       (0x01800000UL)
+
+struct thread_struct {
+  	unsigned long	uregs[5];		/* Only save user r0,r1,r2,r13,r14 */
+  	unsigned long	sregs[11];		/* Only save svc r4-r14 */
+  	unsigned long	fpe_save[140/4];	/* FPE save state */
+  	unsigned long	address,		/* Address of fault */
+  			trap_no,		/* Trap number */
+  			error_code;		/* Error code of trap */
+  	unsigned long	memmap;			/* Memory map */
+  	unsigned long	fs;			/* simulated fs */
+};
+
+#define INIT_MMAP { &init_mm, 0, 0x02000000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
+
+#define INIT_TSS  { \
+	{ 0, }, \
+	{ 0, }, \
+	{ 0, }, \
+	0,0,0, \
+	(long) &swapper_pg_dir, \
+	1 \
+}
+
+static inline void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
+{
+	int i;
+	unsigned long *stack = (unsigned long *)sp;
+	for(i=3; i <15; i++)	/* Initialise all registers to zero */
+	  regs->uregs[i] = 0;
+	regs->ARM_pc = pc;		/* pc */
+	regs->ARM_sp = sp;		/* sp */
+	regs->ARM_r2 = stack[2];	/* r2 (envp) */
+	regs->ARM_r1 = stack[1];	/* r1 (argv) */
+	regs->ARM_r0 = stack[0];	/* r0 (argc) */
+}
+
+/*
+ * Return a saved PC of a blocked thread
+ */
+extern inline unsigned long thread_saved_pc(struct thread_struct *t)
+{
+	return ((unsigned long *)t->sregs[9])[2];
+}
+#endif /* __ASM_ARM_PROCESSOR_H */
+
diff -urNwbB linux/include/asm-arm/ptrace.h linux.arm/include/asm-arm/ptrace.h
--- linux/include/asm-arm/ptrace.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/ptrace.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,37 @@
+#ifndef __ASM_ARM_PTRACE_H
+#define __ASM_ARM_PTRACE_H
+
+
+/* this struct defines the way the registers are stored on the
+   stack during a system call. */
+
+struct pt_regs {
+	long uregs[17];
+};
+
+#define ARM_pc		uregs[15]
+#define ARM_lr		uregs[14]
+#define ARM_sp		uregs[13]
+#define ARM_ip		uregs[12]
+#define ARM_fp		uregs[11]
+#define ARM_r10		uregs[10]
+#define ARM_r9		uregs[9]
+#define ARM_r8		uregs[8]
+#define ARM_r7		uregs[7]
+#define ARM_r6		uregs[6]
+#define ARM_r5		uregs[5]
+#define ARM_r4		uregs[4]
+#define ARM_r3		uregs[3]
+#define ARM_r2		uregs[2]
+#define ARM_r1		uregs[1]
+#define ARM_r0		uregs[0]
+#define ARM_ORIG_r0	uregs[16] /* -1 */
+
+#ifdef __KERNEL__
+#define user_mode(regs)			(((regs)->ARM_pc & 3) == 0)
+#define instruction_pointer(regs)	((regs)->ARM_pc & 0x03fffffc)
+extern void show_regs(struct pt_regs *);
+#endif
+
+#endif
+
diff -urNwbB linux/include/asm-arm/resource.h linux.arm/include/asm-arm/resource.h
--- linux/include/asm-arm/resource.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/resource.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,23 @@
+#ifndef _ARM_RESOURCE_H
+#define _ARM_RESOURCE_H
+
+/*
+ * Resource limits
+ */
+
+#define RLIMIT_CPU	0		/* CPU time in ms */
+#define RLIMIT_FSIZE	1		/* Maximum filesize */
+#define RLIMIT_DATA	2		/* max data size */
+#define RLIMIT_STACK	3		/* max stack size */
+#define RLIMIT_CORE	4		/* max core file size */
+#define RLIMIT_RSS	5		/* max resident set size */
+#define RLIMIT_NPROC	6		/* max number of processes */
+#define RLIMIT_NOFILE	7		/* max number of open files */
+
+#ifdef notdef
+#define RLIMIT_MEMLOCK	8		/* max locked-in-memory address space*/
+#endif
+
+#define RLIM_NLIMITS	8
+
+#endif
diff -urNwbB linux/include/asm-arm/segment.h linux.arm/include/asm-arm/segment.h
--- linux/include/asm-arm/segment.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/segment.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,244 @@
+#ifndef __ASM_ARM_SEGMENT_H
+#define __ASM_ARM_SEGMENT_H
+
+#define KERNEL_CS   0x0
+#define KERNEL_DS   0x0
+
+#define USER_CS     0x1
+#define USER_DS     0x1
+
+#ifndef __ASSEMBLY__
+
+#ifdef NO_INLINE
+#define __INLINE__
+#else
+#define __INLINE__ inline
+#endif
+
+#include <linux/sched.h>
+
+/*
+ * A better way to do this might be to pass to put/get user a flag
+ * indicating the possibilty of kernel memory accesses...  That
+ * would allow gcc to optimize the following routines a bit better
+ * than that which is possible at the moment.
+ */
+#define IS_USER_SEG (current->tss.fs != KERNEL_DS)
+
+/*
+ * Uh, these should become the main single-value transfer routines..
+ * They automatically use the right size if we just have the right
+ * pointer type..
+ */
+#define put_user(x,ptr) __put_user((unsigned long)(x),(ptr),sizeof(*(ptr)))
+#define get_user(ptr) ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr))))
+
+/*
+ * This is a silly but good way to make sure that
+ * the __put_user function is indeed always optimized,
+ * and that we use the correct sizes..
+ */
+extern int bad_user_access_length(void);
+
+/*
+ * dummy pointer type structure.. gcc won't try to do something strange
+ * this way..
+ */
+struct __segment_dummy { unsigned long a[100]; };
+#define __sd(x) ((struct __segment_dummy *) (x))
+#define __const_sd(x) ((const struct __segment_dummy *) (x))
+
+#if defined(__arm2__) || defined(__arm3__)
+/*
+ * These *have* to tell gcc that lr may be clobbered (if there is an abort)
+ * and the psw (if it tries too hard to optimize this
+ */
+static inline void __put_user(unsigned long x, void * y, int size)
+{
+	if (IS_USER_SEG) {
+		switch (size) {
+			case 1:
+				__asm__(
+				" strbt	%0, [%1]\n"
+				: : "r" (x), "r" (y)
+				: "lr", "cc");
+				break;
+			case 2:
+				{ register unsigned long tmp;
+				__asm__ __volatile__(
+				" strbt %1, [%2], #1\n"
+				" mov   %0, %1, lsr #8\n"
+				" strbt %0, [%2]\n"
+				: "=&r" (tmp)
+				: "r" (x), "r" (y)
+				: "2", "lr", "cc");
+				}
+				break;
+			case 4:
+				if ((int)y & 3) {
+				register unsigned long tmp;
+				__asm__ __volatile__(
+				" strbt %1, [%2], #1\n"
+				" mov   %0, %1, lsr #8\n"
+				" strbt %0, [%2], #1\n"
+				" mov   %0, %0, lsr #8\n"
+				" strbt %0, [%2], #1\n"
+				" mov   %0, %0, lsr #8\n"
+				" strbt %0, [%2]\n"
+				: "=&r" (tmp)
+				: "r" (x), "r" (y)
+				: "2", "lr", "cc");
+				} else {
+				__asm__(
+				" strt  %0, [%1]\n"
+				: : "r" (x), "r" (y)
+				: "lr", "cc");
+				}
+				break;
+			default:
+				bad_user_access_length();
+		}
+	} else {
+		switch (size) {
+			case 1: *(unsigned char *)y = x; break;
+			case 2: *(unsigned short *)y = x; break;
+			case 4: *(unsigned long *)y = x; break;
+			default:
+				bad_user_access_length();
+		}
+	}
+}
+
+static inline unsigned long __get_user(const void *y, int size)
+{
+	unsigned long result1, result2;
+
+	if (IS_USER_SEG) {
+		switch (size) {
+			case 1:
+				__asm__(
+				" ldrbt %0, [%1]\n"
+				: "=r" (result1)
+				: "r" (y)
+				: "lr", "cc");
+				return result1;
+			case 2:
+				__asm__(
+				" ldrbt %0, [%2], #1\n"
+				" ldrbt %1, [%2]\n"
+				" orr   %0, %0, %1, lsl #8\n"
+				: "=&r" (result1), "=r" (result2)
+				: "r" (y)
+				: "lr", "cc");
+				return result1;
+			case 4:
+				if ((int)y & 3) {
+				__asm__(
+				" ldrt %0, [%2], #4\n"
+				" ldrt %1, [%2]\n"
+				: "=&r" (result1), "=r" (result2)
+				: "r" ((int)y & ~3)
+				: "lr", "cc");
+				result1 >>= ((int)y & 3)*8;
+				return result1 | (result2 << (32-((int)y & 3)*8));
+				} else {
+				__asm__(
+				" ldrt %0, [%1]\n"
+				: "=r" (result1)
+				: "r" (y)
+				: "lr", "cc");
+				return result1;
+				}
+			default:
+				return bad_user_access_length();
+		}
+	} else {
+		switch (size) {
+			case 1: return *(unsigned char *)y; break;
+			case 2: return *(unsigned short *)y; break;
+			case 4: return *(unsigned long *)y; break;
+			default:
+				return bad_user_access_length();
+		}
+	}
+}		
+#else
+#error Adjust this for your processor
+#endif
+
+/*
+ * these are depreciated..
+ */
+
+static __INLINE__ unsigned char get_user_byte(const char *addr)
+{
+	return __get_user(addr, 1);
+}
+
+#define get_fs_byte(addr) get_user_byte((char *)(addr))
+
+static __INLINE__ unsigned short get_user_word(const short *addr)
+{
+	return __get_user(addr, 2);
+}
+
+#define get_fs_word(addr) get_user_word((short *)(addr))
+
+static __INLINE__ unsigned long get_user_long(const long *addr)
+{
+	return __get_user(addr, 4);
+}
+
+#define get_fs_long(addr) get_user_long((long *)(addr))
+
+static __INLINE__ void put_user_byte(char val,char *addr)
+{
+	__put_user(val, addr, 1);
+}
+
+#define put_fs_byte(x,addr) put_user_byte((x),(char *)(addr))
+
+static __INLINE__ void put_user_word(short val,short *addr)
+{
+	__put_user(val, addr, 2);
+}
+
+#define put_fs_word(x,addr) put_user_word((x),(short *)(addr))
+
+static __INLINE__ void put_user_long(long val,long *addr)
+{
+	__put_user(val, addr, 4);
+}
+
+#define put_fs_long(x,addr) put_user_long((x),(long *)(addr))
+
+
+extern void memcpy_fromfs(void * to, const void * from, unsigned long n);
+extern void __memcpy_tofs(void * to, const void * from, unsigned long n);
+static __INLINE__ void memcpy_tofs(void *to, const void *from, unsigned long n)
+{
+	if(IS_USER_SEG)
+		__memcpy_tofs(to, from, n);
+	else
+		memcpy(to, from, n);
+}
+
+static inline unsigned long get_fs(void)
+{
+	return current->tss.fs;
+}
+
+static inline unsigned long get_ds(void)
+{
+	return KERNEL_DS;
+}
+
+static inline void set_fs(unsigned long val)
+{
+	current->tss.fs = val;
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ARM_SEGMENT_H */
+
diff -urNwbB linux/include/asm-arm/shmparam.h linux.arm/include/asm-arm/shmparam.h
--- linux/include/asm-arm/shmparam.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/shmparam.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,44 @@
+#ifndef _ASMARM_SHMPARAM_H
+#define _ASMARM_SHMPARAM_H
+
+/* address range for shared memory attaches if no address passed to shmat() */
+#define SHM_RANGE_START	0x00a00000
+#define SHM_RANGE_END	0x00c00000
+
+/*
+ * Format of a swap-entry for shared memory pages currently out in
+ * swap space (see also mm/swap.c).
+ *
+ * SWP_TYPE = SHM_SWP_TYPE
+ * SWP_OFFSET is used as follows:
+ *
+ *  bits 0..6 : id of shared memory segment page belongs to (SHM_ID)
+ *  bits 7..21: index of page within shared memory segment (SHM_IDX)
+ *		(actually fewer bits get used since SHMMAX is so low)
+ */
+
+/*
+ * Keep _SHM_ID_BITS as low as possible since SHMMNI depends on it and
+ * there is a static array of size SHMMNI.
+ */
+#define _SHM_ID_BITS	7
+#define SHM_ID_MASK	((1<<_SHM_ID_BITS)-1)
+
+#define SHM_IDX_SHIFT	(_SHM_ID_BITS)
+#define _SHM_IDX_BITS	15
+#define SHM_IDX_MASK	((1<<_SHM_IDX_BITS)-1)
+
+/*
+ * _SHM_ID_BITS + _SHM_IDX_BITS must be <= 24 on the i386 and
+ * SHMMAX <= (PAGE_SIZE << _SHM_IDX_BITS).
+ */
+
+#define SHMMAX 0x3fa000			/* max shared seg size (bytes) */
+#define SHMMIN 1 /* really PAGE_SIZE */	/* min shared seg size (bytes) */
+#define SHMMNI (1<<_SHM_ID_BITS)	/* max num of segs system wide */
+#define SHMALL				/* max shm system wide (pages) */ \
+	(1<<(_SHM_IDX_BITS+_SHM_ID_BITS))
+#define	SHMLBA PAGE_SIZE		/* attach addr a multiple of this */
+#define SHMSEG SHMMNI			/* max shared segs per process */
+
+#endif /* _ASMARM_SHMPARAM_H */
diff -urNwbB linux/include/asm-arm/sigcontext.h linux.arm/include/asm-arm/sigcontext.h
--- linux/include/asm-arm/sigcontext.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/sigcontext.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,12 @@
+#ifndef _ASMARM_SIGCONTEXT_H
+#define _ASMARM_SIGCONTEXT_H
+
+struct sigcontext_struct {
+	unsigned long magic;
+	unsigned long reg[17];
+	unsigned long trap_no;
+	unsigned long error_code;
+	unsigned long oldmask;
+};
+
+#endif
diff -urNwbB linux/include/asm-arm/signal.h linux.arm/include/asm-arm/signal.h
--- linux/include/asm-arm/signal.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/signal.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,95 @@
+#ifndef _ASMARM_SIGNAL_H
+#define _ASMARM_SIGNAL_H
+
+typedef unsigned long sigset_t;		/* at least 32 bits */
+
+#define _NSIG             32
+#define NSIG		_NSIG
+
+#define SIGHUP		 1
+#define SIGINT		 2
+#define SIGQUIT		 3
+#define SIGILL		 4
+#define SIGTRAP		 5
+#define SIGABRT		 6
+#define SIGIOT		 6
+#define SIGBUS		 7
+#define SIGFPE		 8
+#define SIGKILL		 9
+#define SIGUSR1		10
+#define SIGSEGV		11
+#define SIGUSR2		12
+#define SIGPIPE		13
+#define SIGALRM		14
+#define SIGTERM		15
+#define SIGSTKFLT	16
+#define SIGCHLD		17
+#define SIGCONT		18
+#define SIGSTOP		19
+#define SIGTSTP		20
+#define SIGTTIN		21
+#define SIGTTOU		22
+#define SIGURG		23
+#define SIGXCPU		24
+#define SIGXFSZ		25
+#define SIGVTALRM	26
+#define SIGPROF		27
+#define SIGWINCH	28
+#define SIGIO		29
+#define SIGPOLL		SIGIO
+/*
+#define SIGLOST		29
+*/
+#define SIGPWR		30
+#define	SIGUNUSED	31
+
+/*
+ * sa_flags values: SA_STACK is not currently supported, but will allow the
+ * usage of signal stacks by using the (now obsolete) sa_restorer field in
+ * the sigaction structure as a stack pointer. This is now possible due to
+ * the changes in signal handling. LBT 010493.
+ * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the
+ * SA_RESTART flag to get restarting signals (which were the default long ago)
+ */
+#define SA_NOCLDSTOP	1
+#define SA_STACK	0x08000000
+#define SA_RESTART	0x10000000
+#define SA_INTERRUPT	0x20000000
+#define SA_NOMASK	0x40000000
+#define SA_ONESHOT	0x80000000
+
+#ifdef __KERNEL__
+/*
+ * These values of sa_flags are used only by the kernel as part of the
+ * irq handling routines.
+ *
+ * SA_INTERRUPT is also used by the irq handling routines.
+ */
+#define SA_PROBE SA_ONESHOT
+#define SA_SAMPLE_RANDOM SA_RESTART
+#endif
+
+
+#define SIG_BLOCK          0	/* for blocking signals */
+#define SIG_UNBLOCK        1	/* for unblocking signals */
+#define SIG_SETMASK        2	/* for setting the signal mask */
+
+/* Type of a signal handler.  */
+typedef void (*__sighandler_t)(int);
+
+#define SIG_DFL	((__sighandler_t)0)	/* default signal handling */
+#define SIG_IGN	((__sighandler_t)1)	/* ignore signal */
+#define SIG_ERR	((__sighandler_t)-1)	/* error return from signal */
+
+struct sigaction {
+	__sighandler_t sa_handler;
+	sigset_t sa_mask;
+	unsigned long sa_flags;
+	void (*sa_restorer)(void);
+};
+
+#ifdef __KERNEL__
+#include <asm/sigcontext.h>
+#endif
+
+#endif
diff -urNwbB linux/include/asm-arm/socket.h linux.arm/include/asm-arm/socket.h
--- linux/include/asm-arm/socket.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/socket.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,30 @@
+#ifndef _ASMARM_SOCKET_H
+#define _ASMARM_SOCKET_H
+
+/* Socket-level I/O control calls. */
+#define FIOSETOWN 	0x8901
+#define SIOCSPGRP	0x8902
+#define FIOGETOWN	0x8903
+#define SIOCGPGRP	0x8904
+#define SIOCATMARK	0x8905
+#define SIOCGSTAMP	0x8906		/* Get stamp */
+
+/* For setsockoptions(2) */
+#define SOL_SOCKET	1
+
+#define SO_DEBUG	1
+#define SO_REUSEADDR	2
+#define SO_TYPE		3
+#define SO_ERROR	4
+#define SO_DONTROUTE	5
+#define SO_BROADCAST	6
+#define SO_SNDBUF	7
+#define SO_RCVBUF	8
+#define SO_KEEPALIVE	9
+#define SO_OOBINLINE	10
+#define SO_NO_CHECK	11
+#define SO_PRIORITY	12
+#define SO_LINGER	13
+/* To add :#define SO_REUSEPORT 14 */
+
+#endif /* _ASM_SOCKET_H */
diff -urNwbB linux/include/asm-arm/stat.h linux.arm/include/asm-arm/stat.h
--- linux/include/asm-arm/stat.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/stat.h	Sun Feb 11 09:32:56 1996
@@ -0,0 +1,41 @@
+#ifndef _ASMARM_STAT_H
+#define _ASMARM_STAT_H
+
+struct old_stat {
+	unsigned short st_dev;
+	unsigned short st_ino;
+	unsigned short st_mode;
+	unsigned short st_nlink;
+	unsigned short st_uid;
+	unsigned short st_gid;
+	unsigned short st_rdev;
+	unsigned long  st_size;
+	unsigned long  st_atime;
+	unsigned long  st_mtime;
+	unsigned long  st_ctime;
+};
+
+struct new_stat {
+	unsigned short st_dev;
+	unsigned short __pad1;
+	unsigned long st_ino;
+	unsigned short st_mode;
+	unsigned short st_nlink;
+	unsigned short st_uid;
+	unsigned short st_gid;
+	unsigned short st_rdev;
+	unsigned short __pad2;
+	unsigned long  st_size;
+	unsigned long  st_blksize;
+	unsigned long  st_blocks;
+	unsigned long  st_atime;
+	unsigned long  __unused1;
+	unsigned long  st_mtime;
+	unsigned long  __unused2;
+	unsigned long  st_ctime;
+	unsigned long  __unused3;
+	unsigned long  __unused4;
+	unsigned long  __unused5;
+};
+
+#endif
diff -urNwbB linux/include/asm-arm/statfs.h linux.arm/include/asm-arm/statfs.h
--- linux/include/asm-arm/statfs.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/statfs.h	Sun Feb 11 09:32:57 1996
@@ -0,0 +1,21 @@
+#ifndef _ASMARM_STATFS_H
+#define _ASMARM_STATFS_H
+
+typedef struct {
+	long    val[2];
+} fsid_t;
+
+struct statfs {
+	long f_type;
+	long f_bsize;
+	long f_blocks;
+	long f_bfree;
+	long f_bavail;
+	long f_files;
+	long f_ffree;
+	fsid_t f_fsid;
+	long f_namelen;
+	long f_spare[6];
+};
+
+#endif
diff -urNwbB linux/include/asm-arm/string.h linux.arm/include/asm-arm/string.h
--- linux/include/asm-arm/string.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/string.h	Sun Feb 11 09:32:57 1996
@@ -0,0 +1,14 @@
+#ifndef __ASM_ARM_STRING_H
+#define __ASM_ARM_STRING_H
+
+/*
+ * inline versions, hmm...
+ */
+
+#define __HAVE_ARCH_STRRCHR
+#define __HAVE_ARCH_MEMCPY
+#define __HAVE_ARCH_MEMMOVE
+#define __HAVE_ARCH_MEMSET
+
+#endif
+ 
diff -urNwbB linux/include/asm-arm/system.h linux.arm/include/asm-arm/system.h
--- linux/include/asm-arm/system.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/system.h	Sun Feb 11 09:32:57 1996
@@ -0,0 +1,206 @@
+#ifndef __ASM_ARM_SYSTEM_H
+#define __ASM_ARM_SYSTEM_H
+
+#include <linux/kernel.h>
+
+extern void arm_malalignedptr(const char *, void *, volatile void *);
+extern void arm_invalidptr(const char *, void *, int);
+
+/* This special macro can be used to load a debugging register */
+
+#define loaddebug(register)
+
+/*
+ *      switch_to(n) should switch tasks to task nr n, first
+ * checking that n isn't the current task, in which case it does nothing.
+ *
+ * It also reloads the debug regs if necessary..
+ */
+
+extern void switch_to(void *); /* should be struct task_struct, but it isn't always defined */
+
+#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
+#define tas(ptr) (xchg((ptr),1))
+
+struct __xchg_dummy { unsigned long a[100]; };
+#define __xg(x) ((volatile struct __xchg_dummy *)(x))
+
+#if defined (__arm2__)
+static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
+{
+	unsigned long temp0, temp1;
+	switch (size) {
+		case 1: __asm__ __volatile__(
+			" mov   %0, pc\n"
+			" orr   %1, %0, #0x08000000\n"
+			" teqp  %1, #0\n"
+			" mov   %1, %3\n"	/* Safe: in case it uses the same reg */
+			" ldrb  %2, [%4]\n"
+			" strb  %1, [%4]\n"
+			" teqp  %0, #0\n"
+			: "=r" (temp0), "=r" (temp1), "=r" (x)
+			: "r" (x), "r" (ptr)
+			: "memory");
+			break;
+		case 2: { unsigned long temp0, temp1;
+			if (((long)ptr) & 1)
+			{
+				void *pc;
+				__asm__("mov\t%0, pc": "=r" (pc));
+				arm_malalignedptr("xchg", pc, ptr);
+			}
+			__asm__ __volatile__(
+			" mov   %0, pc\n"
+			" orr   %1, %0, #0x08000000\n"
+			" teqp  %1, #0\n"
+			" mov   %1, %3\n"
+			" ldr	%2, [%4]\n"
+			" strb  %1, [%4]\n"
+			" mov   %1, %1, lsr #8\n"
+			" strb  %1, [%4, #1]\n"
+			" teqp  %0, #0\n"
+			: "=r" (temp0), "=r" (temp1), "=r" (x)
+			: "r" (x), "r" (ptr)
+			: "memory");
+			val &= 0xffff;
+			break;
+			}
+		case 4: { unsigned long temp0, temp1;
+			if (((long)ptr) & 3)
+			{
+				void *pc;
+				__asm__("mov\t%0, pc": "=r" (pc));
+				arm_malalignedptr("xchg", pc, ptr);
+			}
+			__asm__ __volatile__(
+			" mov   %0, pc\n"
+			" orr   %1, %0, #0x08000000\n"
+			" teqp  %1, #0\n"
+			" mov   %1, %3\n"
+			" ldr   %2, [%4]\n"
+			" str   %1, [%4]\n"
+			" teqp  %0, #0\n"
+			: "=r" (temp0), "=r" (temp1), "=r" (x)
+			: "r" (x), "r" (ptr)
+			: "memory");
+			}
+			break;
+		default:
+			{
+				void *pc;
+				__asm__("mov\t%0, pc": "=r" (pc));
+				arm_invalidptr("xchg", pc, size);
+				x = 0;
+			}
+	}
+	return x;
+}
+#elif defined (__arm3__)
+static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
+{
+	switch (size) {
+		case 1:
+			__asm__ __volatile__(
+			" swpb   %0, %1, [%2]\n\t"
+			: "=r" (x)
+			: "r" (x), "r" (ptr)
+			: "memory");
+			break;
+		case 2: { unsigned long temp0, temp1;
+			if (((long)ptr) & 1)
+			{
+				void *pc;
+				__asm__("mov\t%0, pc": "=r" (pc));
+				arm_malalignedptr("xchg", pc, ptr);
+			}
+			__asm__ __volatile__(
+			" mov   %0, pc\n"
+			" orr   %1, %0, #0x08000000\n"
+			" teqp  %1, #0\n"
+			" mov   %1, %3\n"
+			" ldr	%2, [%4]\n"
+			" strb  %1, [%4]\n"
+			" mov   %1, %1, lsr #8\n"
+			" strb  %1, [%4, #1]\n"
+			" teqp  %0, #0\n"
+			: "=r" (temp0), "=r" (temp1), "=r" (x)
+			: "r" (x), "r" (ptr)
+			: "memory");
+			x &= 0xffff;
+			break;
+			}
+		case 4: if (((long)ptr) & 3)
+			{
+				void *pc;
+				__asm__("mov\t%0, pc": "=r" (pc));
+				arm_malalignedptr("xchg", pc, ptr);
+			}
+			__asm__ __volatile__(
+			" swp   %0, %1, [%2]\n"
+			: "=r" (x)
+			: "r" (x), "r" (ptr)
+			: "memory");
+			break;
+		default:
+			{
+				void *pc;
+				__asm__("mov\t%0, pc": "=r" (pc));
+				arm_invalidptr("xchg", pc, size);
+				x = 0;
+			}
+	}
+	return x;
+}
+#else
+#error Adjust this routine for your processor
+#endif
+
+#define mb() __asm__ __volatile__ ("" : : : "memory")
+
+#define sti()				\
+	do {				\
+	  unsigned long temp;		\
+	  __asm__ __volatile__(		\
+	  " mov %0, pc\n"		\
+	  " bic %0, %0, #0x08000000\n"	\
+	  " teqp %0, #0\n"		\
+	  : "=r" (temp)			\
+	  : );				\
+	} while(0)
+
+#define cli()				\
+	do {				\
+	  unsigned long temp;		\
+	  __asm__ __volatile__(		\
+	  " mov %0, pc\n"		\
+	  " orr %0, %0, #0x08000000\n"	\
+	  " teqp %0, #0\n"		\
+	  : "=r" (temp)			\
+	  : );				\
+	} while(0)
+
+#define save_flags(x)			\
+	do {				\
+	  __asm__ __volatile__(		\
+	  " mov %0, pc\n"		\
+	  : "=r" (x) : );		\
+	} while (0)
+
+#define restore_flags(x)		\
+	do {				\
+	  unsigned long temp;		\
+	  __asm__ __volatile__(		\
+	  " mov %0, pc\n"		\
+	  " bic %0, %0, #0x08000000\n"	\
+	  " and %1, %1, #0x08000000\n"	\
+	  " orr %0, %0, %1\n"		\
+	  " teqp %0, #0\n"		\
+	  : "=r" (temp)			\
+	  : "r" (x)			\
+	  : "cc");			\
+	} while (0)
+
+#define nop() __asm__ __volatile__("mov r0,r0\n\t");
+
+#endif
+
diff -urNwbB linux/include/asm-arm/termios.h linux.arm/include/asm-arm/termios.h
--- linux/include/asm-arm/termios.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/termios.h	Sun Feb 11 09:32:57 1996
@@ -0,0 +1,299 @@
+#ifndef _ASMARM_TERMIOS_H
+#define _ASMARM_TERMIOS_H
+
+/* 0x54 is just a magic number to make these relatively unique ('T') */
+
+#define TCGETS		0x5401
+#define TCSETS		0x5402
+#define TCSETSW		0x5403
+#define TCSETSF		0x5404
+#define TCGETA		0x5405
+#define TCSETA		0x5406
+#define TCSETAW		0x5407
+#define TCSETAF		0x5408
+#define TCSBRK		0x5409
+#define TCXONC		0x540A
+#define TCFLSH		0x540B
+#define TIOCEXCL	0x540C
+#define TIOCNXCL	0x540D
+#define TIOCSCTTY	0x540E
+#define TIOCGPGRP	0x540F
+#define TIOCSPGRP	0x5410
+#define TIOCOUTQ	0x5411
+#define TIOCSTI		0x5412
+#define TIOCGWINSZ	0x5413
+#define TIOCSWINSZ	0x5414
+#define TIOCMGET	0x5415
+#define TIOCMBIS	0x5416
+#define TIOCMBIC	0x5417
+#define TIOCMSET	0x5418
+#define TIOCGSOFTCAR	0x5419
+#define TIOCSSOFTCAR	0x541A
+#define FIONREAD	0x541B
+#define TIOCINQ		FIONREAD
+#define TIOCLINUX	0x541C
+#define TIOCCONS	0x541D
+#define TIOCGSERIAL	0x541E
+#define TIOCSSERIAL	0x541F
+#define TIOCPKT		0x5420
+#define FIONBIO		0x5421
+#define TIOCNOTTY	0x5422
+#define TIOCSETD	0x5423
+#define TIOCGETD	0x5424
+#define TCSBRKP		0x5425	/* Needed for POSIX tcsendbreak() */
+#define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
+#define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
+#define FIOCLEX		0x5451
+#define FIOASYNC	0x5452
+#define TIOCSERCONFIG	0x5453
+#define TIOCSERGWILD	0x5454
+#define TIOCSERSWILD	0x5455
+#define TIOCGLCKTRMIOS	0x5456
+#define TIOCSLCKTRMIOS	0x5457
+#define TIOCSERGSTRUCT	0x5458 /* For debugging only */
+#define TIOCSERGETLSR   0x5459 /* Get line status register */
+#define TIOCSERGETMULTI 0x545A /* Get multiport config  */
+#define TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+/* Used for packet mode */
+#define TIOCPKT_DATA		 0
+#define TIOCPKT_FLUSHREAD	 1
+#define TIOCPKT_FLUSHWRITE	 2
+#define TIOCPKT_STOP		 4
+#define TIOCPKT_START		 8
+#define TIOCPKT_NOSTOP		16
+#define TIOCPKT_DOSTOP		32
+
+struct winsize {
+	unsigned short ws_row;
+	unsigned short ws_col;
+	unsigned short ws_xpixel;
+	unsigned short ws_ypixel;
+};
+
+#define NCC 8
+struct termio {
+	unsigned short c_iflag;		/* input mode flags */
+	unsigned short c_oflag;		/* output mode flags */
+	unsigned short c_cflag;		/* control mode flags */
+	unsigned short c_lflag;		/* local mode flags */
+	unsigned char c_line;		/* line discipline */
+	unsigned char c_cc[NCC];	/* control characters */
+};
+
+#define NCCS 19
+struct termios {
+	tcflag_t c_iflag;		/* input mode flags */
+	tcflag_t c_oflag;		/* output mode flags */
+	tcflag_t c_cflag;		/* control mode flags */
+	tcflag_t c_lflag;		/* local mode flags */
+	cc_t c_line;			/* line discipline */
+	cc_t c_cc[NCCS];		/* control characters */
+};
+
+/* c_cc characters */
+#define VINTR 0
+#define VQUIT 1
+#define VERASE 2
+#define VKILL 3
+#define VEOF 4
+#define VTIME 5
+#define VMIN 6
+#define VSWTC 7
+#define VSTART 8
+#define VSTOP 9
+#define VSUSP 10
+#define VEOL 11
+#define VREPRINT 12
+#define VDISCARD 13
+#define VWERASE 14
+#define VLNEXT 15
+#define VEOL2 16
+
+#ifdef __KERNEL__
+/*	intr=^C		quit=^|		erase=del	kill=^U
+	eof=^D		vtime=\0	vmin=\1		sxtc=\0
+	start=^Q	stop=^S		susp=^Z		eol=\0
+	reprint=^R	discard=^U	werase=^W	lnext=^V
+	eol2=\0
+*/
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#endif
+
+/* c_iflag bits */
+#define IGNBRK	0000001
+#define BRKINT	0000002
+#define IGNPAR	0000004
+#define PARMRK	0000010
+#define INPCK	0000020
+#define ISTRIP	0000040
+#define INLCR	0000100
+#define IGNCR	0000200
+#define ICRNL	0000400
+#define IUCLC	0001000
+#define IXON	0002000
+#define IXANY	0004000
+#define IXOFF	0010000
+#define IMAXBEL	0020000
+
+/* c_oflag bits */
+#define OPOST	0000001
+#define OLCUC	0000002
+#define ONLCR	0000004
+#define OCRNL	0000010
+#define ONOCR	0000020
+#define ONLRET	0000040
+#define OFILL	0000100
+#define OFDEL	0000200
+#define NLDLY	0000400
+#define   NL0	0000000
+#define   NL1	0000400
+#define CRDLY	0003000
+#define   CR0	0000000
+#define   CR1	0001000
+#define   CR2	0002000
+#define   CR3	0003000
+#define TABDLY	0014000
+#define   TAB0	0000000
+#define   TAB1	0004000
+#define   TAB2	0010000
+#define   TAB3	0014000
+#define   XTABS	0014000
+#define BSDLY	0020000
+#define   BS0	0000000
+#define   BS1	0020000
+#define VTDLY	0040000
+#define   VT0	0000000
+#define   VT1	0040000
+#define FFDLY	0100000
+#define   FF0	0000000
+#define   FF1	0100000
+
+/* c_cflag bit meaning */
+#define CBAUD	0010017
+#define  B0	0000000		/* hang up */
+#define  B50	0000001
+#define  B75	0000002
+#define  B110	0000003
+#define  B134	0000004
+#define  B150	0000005
+#define  B200	0000006
+#define  B300	0000007
+#define  B600	0000010
+#define  B1200	0000011
+#define  B1800	0000012
+#define  B2400	0000013
+#define  B4800	0000014
+#define  B9600	0000015
+#define  B19200	0000016
+#define  B38400	0000017
+#define EXTA B19200
+#define EXTB B38400
+#define CSIZE	0000060
+#define   CS5	0000000
+#define   CS6	0000020
+#define   CS7	0000040
+#define   CS8	0000060
+#define CSTOPB	0000100
+#define CREAD	0000200
+#define PARENB	0000400
+#define PARODD	0001000
+#define HUPCL	0002000
+#define CLOCAL	0004000
+#define CBAUDEX 0010000
+#define  B57600  0010001
+#define  B115200 0010002
+#define  B230400 0010003
+#define CIBAUD	  002003600000	/* input baud rate (not used) */
+#define CRTSCTS	  020000000000		/* flow control */
+
+/* c_lflag bits */
+#define ISIG	0000001
+#define ICANON	0000002
+#define XCASE	0000004
+#define ECHO	0000010
+#define ECHOE	0000020
+#define ECHOK	0000040
+#define ECHONL	0000100
+#define NOFLSH	0000200
+#define TOSTOP	0000400
+#define ECHOCTL	0001000
+#define ECHOPRT	0002000
+#define ECHOKE	0004000
+#define FLUSHO	0010000
+#define PENDIN	0040000
+#define IEXTEN	0100000
+
+/* modem lines */
+#define TIOCM_LE	0x001
+#define TIOCM_DTR	0x002
+#define TIOCM_RTS	0x004
+#define TIOCM_ST	0x008
+#define TIOCM_SR	0x010
+#define TIOCM_CTS	0x020
+#define TIOCM_CAR	0x040
+#define TIOCM_RNG	0x080
+#define TIOCM_DSR	0x100
+#define TIOCM_CD	TIOCM_CAR
+#define TIOCM_RI	TIOCM_RNG
+
+/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+#define TIOCSER_TEMT    0x01	/* Transmitter physically empty */
+
+
+/* tcflow() and TCXONC use these */
+#define	TCOOFF		0
+#define	TCOON		1
+#define	TCIOFF		2
+#define	TCION		3
+
+/* tcflush() and TCFLSH use these */
+#define	TCIFLUSH	0
+#define	TCOFLUSH	1
+#define	TCIOFLUSH	2
+
+/* tcsetattr uses these */
+#define	TCSANOW		0
+#define	TCSADRAIN	1
+#define	TCSAFLUSH	2
+
+/* line disciplines */
+#define N_TTY		0
+#define N_SLIP		1
+#define N_MOUSE		2
+#define N_PPP		3
+
+#ifdef __KERNEL__
+
+/*
+ * Translate a "termio" structure into a "termios". Ugh.
+ */
+extern inline void trans_from_termio(struct termio * termio,
+	struct termios * termios)
+{
+#define SET_LOW_BITS(x,y)	((x) = (0xffff0000 & (x)) | (y))
+	SET_LOW_BITS(termios->c_iflag, termio->c_iflag);
+	SET_LOW_BITS(termios->c_oflag, termio->c_oflag);
+	SET_LOW_BITS(termios->c_cflag, termio->c_cflag);
+	SET_LOW_BITS(termios->c_lflag, termio->c_lflag);
+#undef SET_LOW_BITS
+	memcpy(termios->c_cc, termio->c_cc, NCC);
+}
+
+/*
+ * Translate a "termios" structure into a "termio". Ugh.
+ */
+extern inline void trans_to_termio(struct termios * termios,
+	struct termio * termio)
+{
+	termio->c_iflag = termios->c_iflag;
+	termio->c_oflag = termios->c_oflag;
+	termio->c_cflag = termios->c_cflag;
+	termio->c_lflag = termios->c_lflag;
+	termio->c_line	= termios->c_line;
+	memcpy(termio->c_cc, termios->c_cc, NCC);
+}
+
+#endif	/* __KERNEL__ */
+
+#endif	/* _ASMARM_TERMIOS_H */
diff -urNwbB linux/include/asm-arm/types.h linux.arm/include/asm-arm/types.h
--- linux/include/asm-arm/types.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/types.h	Sun Feb 11 09:32:57 1996
@@ -0,0 +1,101 @@
+#ifndef __ASM_ARM_TYPES_H
+#define __ASM_ARM_TYPES_H
+
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef unsigned int size_t;
+#endif
+
+#ifndef _SSIZE_T
+#define _SSIZE_T
+typedef int ssize_t;
+#endif
+
+#ifndef _PTRDIFF_T
+#define _PTRDIFF_T
+typedef int ptrdiff_t;
+#endif
+
+#ifndef _TIME_T
+#define _TIME_T
+typedef long time_t;
+#endif
+
+#ifndef _CLOCK_T
+#define _CLOCK_T
+typedef long clock_t;
+#endif
+
+typedef int pid_t;
+typedef unsigned short uid_t;
+typedef unsigned short gid_t;
+typedef unsigned short dev_t;
+typedef unsigned long ino_t;
+typedef unsigned short mode_t;
+typedef unsigned short umode_t;
+typedef unsigned short nlink_t;
+typedef int daddr_t;
+typedef long off_t;
+
+/*
+ * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the
+ * header files exported to user space
+ */
+
+typedef __signed__ char __s8;
+typedef unsigned char __u8;
+
+typedef __signed__ short __s16;
+typedef unsigned short __u16;
+
+typedef __signed__ long __s32;
+typedef unsigned long __u32;
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+#endif
+
+/*
+ * These aren't exported outside the kernel to avoid name space clashes
+ */
+#ifdef __KERNEL__
+
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed long s32;
+typedef unsigned long u32;
+
+typedef signed long long s64;
+typedef unsigned long long u64;
+
+#endif /* __KERNEL__ */
+
+#undef  __FD_SET
+#define __FD_SET(fd, fdsetp) \
+		(((fd_set *)fdsetp)->fds_bits[fd >> 5] |= (1<<fd))
+
+#undef  __FD_CLR
+#define __FD_CLR(fd, fdsetp) \
+		(((fd_set *)fdsetp)->fds_bits[fd >> 5] &= ~(1<<fd))
+
+#undef  __FD_ISSET
+#define __FD_ISSET(fd, fdsetp) \
+		((((fd_set *)fdsetp)->fds_bits[fd >> 5] & (1<<fd)) != 0)
+
+#undef  __FD_ZERO
+#define __FD_ZERO(fdsetp) \
+		({ ((fd_set *)fdsetp)->fds_bits[0] =  \
+		   ((fd_set *)fdsetp)->fds_bits[1] =  \
+		   ((fd_set *)fdsetp)->fds_bits[2] =  \
+		   ((fd_set *)fdsetp)->fds_bits[3] =  \
+		   ((fd_set *)fdsetp)->fds_bits[4] =  \
+		   ((fd_set *)fdsetp)->fds_bits[5] =  \
+		   ((fd_set *)fdsetp)->fds_bits[6] =  \
+		   ((fd_set *)fdsetp)->fds_bits[7] = 0; })
+#endif
+
diff -urNwbB linux/include/asm-arm/unistd.h linux.arm/include/asm-arm/unistd.h
--- linux/include/asm-arm/unistd.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/unistd.h	Sun Feb 11 09:32:57 1996
@@ -0,0 +1,320 @@
+#ifndef __ASM_ARM_UNISTD_H
+#define __ASM_ARM_UNISTD_H
+
+#define __NR_SYSCALL_BASE	0x900000
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#define __NR_setup		__NR_SYSCALL_BASE+  0	/* used only by init, to get system going */
+#define __NR_exit		__NR_SYSCALL_BASE+  1
+#define __NR_fork		__NR_SYSCALL_BASE+  2
+#define __NR_read		__NR_SYSCALL_BASE+  3
+#define __NR_write		__NR_SYSCALL_BASE+  4
+#define __NR_open		__NR_SYSCALL_BASE+  5
+#define __NR_close		__NR_SYSCALL_BASE+  6
+#define __NR_waitpid		__NR_SYSCALL_BASE+  7
+#define __NR_creat		__NR_SYSCALL_BASE+  8
+#define __NR_link		__NR_SYSCALL_BASE+  9
+#define __NR_unlink		__NR_SYSCALL_BASE+ 10
+#define __NR_execve		__NR_SYSCALL_BASE+ 11
+#define __NR_chdir		__NR_SYSCALL_BASE+ 12
+#define __NR_time		__NR_SYSCALL_BASE+ 13
+#define __NR_mknod		__NR_SYSCALL_BASE+ 14
+#define __NR_chmod		__NR_SYSCALL_BASE+ 15
+#define __NR_chown		__NR_SYSCALL_BASE+ 16
+#define __NR_break		__NR_SYSCALL_BASE+ 17
+#define __NR_oldstat		__NR_SYSCALL_BASE+ 18
+#define __NR_lseek		__NR_SYSCALL_BASE+ 19
+#define __NR_getpid		__NR_SYSCALL_BASE+ 20
+#define __NR_mount		__NR_SYSCALL_BASE+ 21
+#define __NR_umount		__NR_SYSCALL_BASE+ 22
+#define __NR_setuid		__NR_SYSCALL_BASE+ 23
+#define __NR_getuid		__NR_SYSCALL_BASE+ 24
+#define __NR_stime		__NR_SYSCALL_BASE+ 25
+#define __NR_ptrace		__NR_SYSCALL_BASE+ 26
+#define __NR_alarm		__NR_SYSCALL_BASE+ 27
+#define __NR_oldfstat		__NR_SYSCALL_BASE+ 28
+#define __NR_pause		__NR_SYSCALL_BASE+ 29
+#define __NR_utime		__NR_SYSCALL_BASE+ 30
+#define __NR_stty		__NR_SYSCALL_BASE+ 31
+#define __NR_gtty		__NR_SYSCALL_BASE+ 32
+#define __NR_access		__NR_SYSCALL_BASE+ 33
+#define __NR_nice		__NR_SYSCALL_BASE+ 34
+#define __NR_ftime		__NR_SYSCALL_BASE+ 35
+#define __NR_sync		__NR_SYSCALL_BASE+ 36
+#define __NR_kill		__NR_SYSCALL_BASE+ 37
+#define __NR_rename		__NR_SYSCALL_BASE+ 38
+#define __NR_mkdir		__NR_SYSCALL_BASE+ 39
+#define __NR_rmdir		__NR_SYSCALL_BASE+ 40
+#define __NR_dup		__NR_SYSCALL_BASE+ 41
+#define __NR_pipe		__NR_SYSCALL_BASE+ 42
+#define __NR_times		__NR_SYSCALL_BASE+ 43
+#define __NR_prof		__NR_SYSCALL_BASE+ 44
+#define __NR_brk		__NR_SYSCALL_BASE+ 45
+#define __NR_setgid		__NR_SYSCALL_BASE+ 46
+#define __NR_getgid		__NR_SYSCALL_BASE+ 47
+#define __NR_signal		__NR_SYSCALL_BASE+ 48
+#define __NR_geteuid		__NR_SYSCALL_BASE+ 49
+#define __NR_getegid		__NR_SYSCALL_BASE+ 50
+#define __NR_acct		__NR_SYSCALL_BASE+ 51
+#define __NR_phys		__NR_SYSCALL_BASE+ 52
+#define __NR_lock		__NR_SYSCALL_BASE+ 53
+#define __NR_ioctl		__NR_SYSCALL_BASE+ 54
+#define __NR_fcntl		__NR_SYSCALL_BASE+ 55
+#define __NR_mpx		__NR_SYSCALL_BASE+ 56
+#define __NR_setpgid		__NR_SYSCALL_BASE+ 57
+#define __NR_ulimit		__NR_SYSCALL_BASE+ 58
+#define __NR_oldolduname	__NR_SYSCALL_BASE+ 59
+#define __NR_umask		__NR_SYSCALL_BASE+ 60
+#define __NR_chroot		__NR_SYSCALL_BASE+ 61
+#define __NR_ustat		__NR_SYSCALL_BASE+ 62
+#define __NR_dup2		__NR_SYSCALL_BASE+ 63
+#define __NR_getppid		__NR_SYSCALL_BASE+ 64
+#define __NR_getpgrp		__NR_SYSCALL_BASE+ 65
+#define __NR_setsid		__NR_SYSCALL_BASE+ 66
+#define __NR_sigaction		__NR_SYSCALL_BASE+ 67
+#define __NR_sgetmask		__NR_SYSCALL_BASE+ 68
+#define __NR_ssetmask		__NR_SYSCALL_BASE+ 69
+#define __NR_setreuid		__NR_SYSCALL_BASE+ 70
+#define __NR_setregid		__NR_SYSCALL_BASE+ 71
+#define __NR_sigsuspend		__NR_SYSCALL_BASE+ 72
+#define __NR_sigpending		__NR_SYSCALL_BASE+ 73
+#define __NR_sethostname	__NR_SYSCALL_BASE+ 74
+#define __NR_setrlimit		__NR_SYSCALL_BASE+ 75
+#define __NR_getrlimit		__NR_SYSCALL_BASE+ 76
+#define __NR_getrusage		__NR_SYSCALL_BASE+ 77
+#define __NR_gettimeofday	__NR_SYSCALL_BASE+ 78
+#define __NR_settimeofday	__NR_SYSCALL_BASE+ 79
+#define __NR_getgroups		__NR_SYSCALL_BASE+ 80
+#define __NR_setgroups		__NR_SYSCALL_BASE+ 81
+#define __NR_select		__NR_SYSCALL_BASE+ 82
+#define __NR_symlink		__NR_SYSCALL_BASE+ 83
+#define __NR_oldlstat		__NR_SYSCALL_BASE+ 84
+#define __NR_readlink		__NR_SYSCALL_BASE+ 85
+#define __NR_uselib		__NR_SYSCALL_BASE+ 86
+#define __NR_swapon		__NR_SYSCALL_BASE+ 87
+#define __NR_reboot		__NR_SYSCALL_BASE+ 88
+#define __NR_readdir		__NR_SYSCALL_BASE+ 89
+#define __NR_mmap		__NR_SYSCALL_BASE+ 90
+#define __NR_munmap		__NR_SYSCALL_BASE+ 91
+#define __NR_truncate		__NR_SYSCALL_BASE+ 92
+#define __NR_ftruncate		__NR_SYSCALL_BASE+ 93
+#define __NR_fchmod		__NR_SYSCALL_BASE+ 94
+#define __NR_fchown		__NR_SYSCALL_BASE+ 95
+#define __NR_getpriority	__NR_SYSCALL_BASE+ 96
+#define __NR_setpriority	__NR_SYSCALL_BASE+ 97
+#define __NR_profil		__NR_SYSCALL_BASE+ 98
+#define __NR_statfs		__NR_SYSCALL_BASE+ 99
+#define __NR_fstatfs		__NR_SYSCALL_BASE+100
+#define __NR_ioperm		__NR_SYSCALL_BASE+101
+#define __NR_socketcall		__NR_SYSCALL_BASE+102
+#define __NR_syslog		__NR_SYSCALL_BASE+103
+#define __NR_setitimer		__NR_SYSCALL_BASE+104
+#define __NR_getitimer		__NR_SYSCALL_BASE+105
+#define __NR_stat		__NR_SYSCALL_BASE+106
+#define __NR_lstat		__NR_SYSCALL_BASE+107
+#define __NR_fstat		__NR_SYSCALL_BASE+108
+#define __NR_olduname		__NR_SYSCALL_BASE+109
+#define __NR_iopl		__NR_SYSCALL_BASE+110
+#define __NR_vhangup		__NR_SYSCALL_BASE+111
+#define __NR_idle		__NR_SYSCALL_BASE+112
+#define __NR_vm86		__NR_SYSCALL_BASE+113
+#define __NR_wait4		__NR_SYSCALL_BASE+114
+#define __NR_swapoff		__NR_SYSCALL_BASE+115
+#define __NR_sysinfo		__NR_SYSCALL_BASE+116
+#define __NR_ipc		__NR_SYSCALL_BASE+117
+#define __NR_fsync		__NR_SYSCALL_BASE+118
+#define __NR_sigreturn		__NR_SYSCALL_BASE+119
+#define __NR_clone		__NR_SYSCALL_BASE+120
+#define __NR_setdomainname	__NR_SYSCALL_BASE+121
+#define __NR_uname		__NR_SYSCALL_BASE+122
+#define __NR_modify_ldt		__NR_SYSCALL_BASE+123
+#define __NR_adjtimex		__NR_SYSCALL_BASE+124
+#define __NR_mprotect		__NR_SYSCALL_BASE+125
+#define __NR_sigprocmask	__NR_SYSCALL_BASE+126
+#define __NR_create_module	__NR_SYSCALL_BASE+127
+#define __NR_init_module	__NR_SYSCALL_BASE+128
+#define __NR_delete_module	__NR_SYSCALL_BASE+129
+#define __NR_get_kernel_syms	__NR_SYSCALL_BASE+130
+#define __NR_quotactl		__NR_SYSCALL_BASE+131
+#define __NR_getpgid		__NR_SYSCALL_BASE+132
+#define __NR_fchdir		__NR_SYSCALL_BASE+133
+#define __NR_bdflush		__NR_SYSCALL_BASE+134
+#define __NR_sysfs		__NR_SYSCALL_BASE+135
+#define __NR_personality	__NR_SYSCALL_BASE+136
+#define __NR_afs_syscall	__NR_SYSCALL_BASE+137 /* Syscall for Andrew File System */
+#define __NR_setfsuid		__NR_SYSCALL_BASE+138
+#define __NR_setfsgid		__NR_SYSCALL_BASE+139
+#define __NR__llseek		__NR_SYSCALL_BASE+140
+#define __NR_getdents		__NR_SYSCALL_BASE+141
+#define __NR__newselect		__NR_SYSCALL_BASE+142
+#define __NR_flock		__NR_SYSCALL_BASE+143
+
+#define __sys2(x) #x
+#define __sys1(x) __sys2(x)
+#define __syscall(name) "swi " __sys2(name) "\n\t"
+
+#define _syscall0(type,name)								\
+type name(void) {									\
+  long __res;										\
+  __asm__ __volatile__ (								\
+  __syscall(__NR_##name)								\
+  "mov %0,r0\n\t"									\
+  :"=r" (__res) : : "r0","r1","r2","r3");						\
+  if (__res >= 0)									\
+    return (type) __res;								\
+  errno = -__res;									\
+  return -1;										\
+}
+
+#define _syscall1(type,name,type1,arg1)							\
+type name(type1 arg1) {									\
+  long __res;										\
+  __asm__ __volatile__ (								\
+  "mov r0,%1\n\t"									\
+  __syscall(__NR_##name)								\
+  "mov %0,r0\n\t"									\
+        : "=r" (__res)									\
+        : "r" ((long)(arg1))								\
+	: "r0","r1","r2","r3");								\
+  if (__res >= 0)									\
+    return (type) __res;								\
+  errno = -__res;									\
+  return -1;										\
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2)					\
+type name(type1 arg1,type2 arg2) {							\
+  long __res;										\
+  __asm__ __volatile__ (								\
+  "mov r0,%1\n\t"									\
+  "mov r1,%2\n\t"									\
+  __syscall(__NR_##name)								\
+  "mov %0,r0\n\t"									\
+        : "=r" (__res)									\
+        : "r" ((long)(arg1)),"r" ((long)(arg2))						\
+	: "r0","r1","r2","r3");								\
+  if (__res >= 0)									\
+    return (type) __res;								\
+  errno = -__res;									\
+  return -1;										\
+}
+
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)				\
+type name(type1 arg1,type2 arg2,type3 arg3) {						\
+  long __res;										\
+  __asm__ __volatile__ (								\
+  "mov r0,%1\n\t"									\
+  "mov r1,%2\n\t"									\
+  "mov r2,%3\n\t"									\
+  __syscall(__NR_##name)								\
+  "mov %0,r0\n\t"									\
+        : "=r" (__res)									\
+        : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3))			\
+        : "r0","r1","r2","r3");								\
+  if (__res >= 0)									\
+    return (type) __res;								\
+  errno=-__res;										\
+  return -1;										\
+}
+
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)		\
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) {				\
+  long __res;										\
+  __asm__ __volatile__ (								\
+  "mov r0,%1\n\t"									\
+  "mov r1,%2\n\t"									\
+  "mov r2,%3\n\t"									\
+  "mov r3,%4\n\t"									\
+  __syscall(__NR_##name)								\
+  "mov %0,r0\n\t"									\
+  	: "=r" (__res)									\
+  	: "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4))	\
+  	: "r0","r1","r2","r3");								\
+  if (__res >= 0)									\
+    return (type) __res;								\
+  errno=-__res;										\
+  return -1;										\
+}
+  
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5)	\
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) {			\
+  long __res;										\
+  __asm__ __volatile__ (								\
+  "mov r0,%1\n\t"									\
+  "mov r1,%2\n\t"									\
+  "mov r2,%3\n\t"									\
+  "str %5,[sp,#-4]!\n\t"								\
+  "str %4,[sp,#-4]!\n\t"								\
+  "mov r3,sp\n\t"									\
+  __syscall(__NR_##name)								\
+  "mov %0,r0\n\t"									\
+  "add sp,sp,#8\n\t"									\
+  	: "=r" (__res)									\
+  	: "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)),	\
+	  "r" ((long)(arg5))								\
+	: "r0","r1","r2","r3");								\
+  if (__res >= 0)									\
+    return (type) __res;								\
+  errno=-__res;										\
+  return -1;										\
+}
+
+#ifdef __KERNEL_SYSCALLS__
+#define __NR__exit __NR_exit
+static inline _syscall0(int,idle);
+static inline _syscall0(int,fork);
+static inline _syscall2(int,clone,unsigned long,flags,char *,esp);
+static inline _syscall0(int,pause);
+static inline _syscall0(int,setup);
+static inline _syscall0(int,sync);
+static inline _syscall0(pid_t,setsid);
+static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count);
+static inline _syscall1(int,dup,int,fd);
+static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp);
+static inline _syscall3(int,open,const char *,file,int,flag,int,mode);
+static inline _syscall1(int,close,int,fd);
+static inline _syscall1(int,_exit,int,exitcode);
+static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options);
+
+static inline pid_t wait(int * wait_stat)
+{
+	return waitpid(-1,wait_stat,0);
+}
+
+
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+static inline pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+	long retval;
+	extern void move_to_user_mode (void);
+
+	move_to_user_mode();
+
+	retval = clone(flags | CLONE_VM, NULL);
+	
+	if (retval == 0)
+		_exit(fn (arg));
+	
+	return retval;
+}
+
+#endif
+
+#endif /* __ASM_ARM_UNISTD_H */
+
+
+
diff -urNwbB linux/include/asm-arm/user.h linux.arm/include/asm-arm/user.h
--- linux/include/asm-arm/user.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/user.h	Sun Feb 11 09:32:58 1996
@@ -0,0 +1,65 @@
+#ifndef _ARM_USER_H
+#define _ARM_USER_H
+
+#include <asm/page.h>
+#include <linux/ptrace.h>
+/* Core file format: The core file is written in such a way that gdb
+   can understand it and provide useful information to the user (under
+   linux we use the 'trad-core' bfd).  There are quite a number of
+   obstacles to being able to view the contents of the floating point
+   registers, and until these are solved you will not be able to view the
+   contents of them.  Actually, you can read in the core file and look at
+   the contents of the user struct to find out what the floating point
+   registers contain.
+   The actual file contents are as follows:
+   UPAGE: 1 page consisting of a user struct that tells gdb what is present
+   in the file.  Directly after this is a copy of the task_struct, which
+   is currently not used by gdb, but it may come in useful at some point.
+   All of the registers are stored as part of the upage.  The upage should
+   always be only one page.
+   DATA: The data area is stored.  We use current->end_text to
+   current->brk to pick up all of the user variables, plus any memory
+   that may have been malloced.  No attempt is made to determine if a page
+   is demand-zero or if a page is totally unused, we just cover the entire
+   range.  All of the addresses are rounded in such a way that an integral
+   number of pages is written.
+   STACK: We need the stack information in order to get a meaningful
+   backtrace.  We need to write the data from (esp) to
+   current->start_stack, so we round each of these off in order to be able
+   to write an integer number of pages.
+   The minimum core file size is 3 pages, or 12288 bytes.
+*/
+
+/* When the kernel dumps core, it starts by dumping the user struct -
+   this will be used by gdb to figure out where the data and stack segments
+   are within the file, and what virtual addresses to use. */
+struct user{
+/* We start with the registers, to mimic the way that "memory" is returned
+   from the ptrace(3,...) function.  */
+  struct pt_regs regs;		/* Where the registers are actually stored */
+/* ptrace does not yet supply these.  Someday.... */
+  int u_fpvalid;		/* True if math co-processor being used. */
+                                /* for this mess. Not yet used. */
+/* The rest of this junk is to help gdb figure out what goes where */
+  unsigned long int u_tsize;	/* Text segment size (pages). */
+  unsigned long int u_dsize;	/* Data segment size (pages). */
+  unsigned long int u_ssize;	/* Stack segment size (pages). */
+  unsigned long start_code;     /* Starting virtual address of text. */
+  unsigned long start_stack;	/* Starting virtual address of stack area.
+				   This is actually the bottom of the stack,
+				   the top of the stack is always found in the
+				   esp register.  */
+  long int signal;     		/* Signal that caused the core dump. */
+  int reserved;			/* No longer used */
+  struct pt_regs * u_ar0;	/* Used by gdb to help find the values for */
+				/* the registers. */
+  unsigned long magic;		/* To uniquely identify a core file */
+  char u_comm[32];		/* User command that was responsible */
+  int u_debugreg[8];
+};
+#define NBPG PAGE_SIZE
+#define UPAGES 1
+#define HOST_TEXT_START_ADDR (u.start_code)
+#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG)
+
+#endif /* _ARM_USER_H */
diff -urNwbB linux/include/asm-arm/vt.h linux.arm/include/asm-arm/vt.h
--- linux/include/asm-arm/vt.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/vt.h	Sun Feb 11 09:32:58 1996
@@ -0,0 +1,8 @@
+#ifndef _ASMARM_VT_H
+#define _ASMARM_VT_H
+
+#define VT_GETSCRINFO	0x56FD	/* get screen info */
+#define VT_GETPALETTE	0x56FE  /* get palette */
+#define VT_SETPALETTE	0x56FF  /* set palette */
+
+#endif /* _ASMARM_VT_H */
diff -urNwbB linux/include/linux/icmp.h linux.arm/include/linux/icmp.h
--- linux/include/linux/icmp.h	Wed Jul 12 04:41:59 1995
+++ linux.arm/include/linux/icmp.h	Sun Feb 11 09:32:58 1996
@@ -61,7 +61,11 @@
 struct icmphdr {
   __u8		type;
   __u8		code;
+#ifndef __arm__
   __u16		checksum;
+#else
+  __volatile__ __u16 checksum;
+#endif
   union {
 	struct {
 		__u16	id;
diff -urNwbB linux/include/linux/if_ppp.h linux.arm/include/linux/if_ppp.h
--- linux/include/linux/if_ppp.h	Sun Mar  3 11:40:35 1996
+++ linux.arm/include/linux/if_ppp.h	Sun Feb 25 13:41:33 1996
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 3==
+ *  ==FILEVERSION 5==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -35,6 +35,12 @@
 #ifndef _IF_PPP_H_
 #define _IF_PPP_H_
 
+#if defined(__linux__)
+#include <linux/if.h>
+#include <linux/ioctl.h>
+#include <linux/ppp_defs.h>
+#endif
+
 /*
  * Packet sizes
  */
@@ -44,6 +50,8 @@
 #define PPP_VERSION	"2.2.0"
 #define PPP_MAGIC	0x5002	/* Magic value for the ppp structure */
 #define PROTO_IPX	0x002b	/* protocol numbers */
+#define PROTO_DNA_RT    0x0027  /* DNA Routing */
+
 
 /*
  * Bit definitions for flags.
diff -urNwbB linux/include/linux/ip.h linux.arm/include/linux/ip.h
--- linux/include/linux/ip.h	Sun Mar  3 11:38:12 1996
+++ linux.arm/include/linux/ip.h	Sun Feb 11 09:32:59 1996
@@ -103,7 +103,11 @@
 	__u16	frag_off;
 	__u8	ttl;
 	__u8	protocol;
+#ifndef __arm__
 	__u16	check;
+#else
+	__volatile__ __u16 check;
+#endif
 	__u32	saddr;
 	__u32	daddr;
 	/*The options start here. */
diff -urNwbB linux/include/linux/ppp_defs.h linux.arm/include/linux/ppp_defs.h
--- linux/include/linux/ppp_defs.h	Sun Mar  3 11:40:37 1996
+++ linux.arm/include/linux/ppp_defs.h	Sat Feb 24 09:37:27 1996
@@ -28,7 +28,7 @@
  */
 
 /*
- *  ==FILEVERSION 4==
+ *  ==FILEVERSION 5==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, increment the number above.
@@ -66,10 +66,12 @@
  * Protocol field values.
  */
 #define PPP_IP		0x21	/* Internet Protocol */
+#define PPP_IPX		0x2b	/* IPX protocol */
 #define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
 #define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
 #define PPP_COMP	0xfd	/* compressed packet */
 #define PPP_IPCP	0x8021	/* IP Control Protocol */
+#define PPP_IPXCP	0x802b	/* IPX Control Protocol */
 #define PPP_CCP		0x80fd	/* Compression Control Protocol */
 #define PPP_LCP		0xc021	/* Link Control Protocol */
 #define PPP_PAP		0xc023	/* Password Authentication Protocol */
@@ -79,12 +81,16 @@
 /*
  * A 32-bit unsigned integral type.
  */
+#if 0
 #ifndef __BIT_TYPES_DEFINED__
 #ifdef	UINT32_T
 typedef UINT32_T	u_int32_t;
 #else
 typedef unsigned int	u_int32_t;
 #endif
+#endif
+#else
+#include <sys/types.h>   /* This defines u_int32_t */
 #endif
 
 /*
diff -urNwbB linux/include/linux/smb.h linux.arm/include/linux/smb.h
--- linux/include/linux/smb.h	Sat Jul  1 17:00:19 1995
+++ linux.arm/include/linux/smb.h	Sun Feb 11 09:32:59 1996
@@ -31,7 +31,11 @@
 /*
  * Set/Get values in SMB-byte order
  */
+#ifndef __arm__
 #define ARCH i386
+#else
+#define ARCH arm
+#endif
 #if (ARCH == i386)
 #define BVAL(p,off)      (*((byte  *)(((void *)p)+off)))
 #define WVAL(p,off)      (*((word  *)(((void *)p)+off)))
@@ -43,6 +47,57 @@
 /* where to find the base of the SMB packet proper */
 #define smb_base(buf) ((byte *)(((byte *)(buf))+4))
 
+#elif (ARCH == arm)
+static inline byte BVAL(void *p, int off)
+{
+	unsigned char *cp = (unsigned char *)p;
+	return cp[off];
+}
+
+static inline word WVAL(void *p, int off)
+{
+	unsigned char *cp = (unsigned char *)p;
+	return cp[off] | cp[off+1] << 8;
+}
+
+static inline dword DVAL(void *p, int off)
+{
+	unsigned char *cp = (unsigned char *)p + off;
+	if ((int)cp & 3)
+		return cp[0] | cp[1]<<8 | cp[2]<<16 | cp[3]<<24;
+	else
+		return *(unsigned long *)cp;
+}
+
+static inline void BSET(void *p, int off, byte n)
+{
+	unsigned char *cp = (unsigned char *)p;
+	cp[off] = n;
+}
+
+static inline void WSET(void *p, int off, word n)
+{
+	unsigned char *cp = (unsigned char *)p;
+	cp[off] = n & 255;
+	cp[off+1] = n>>8;
+}
+
+static inline void DSET(void *p, int off, dword n)
+{
+	unsigned char *cp = (unsigned char *)p + off;
+	if ((int)cp & 3)
+	{
+		cp[0] = n;
+		cp[1] = n>>8;
+		cp[2] = n>>16;
+		cp[3] = n>>24;
+	}
+	else
+		*(unsigned long *)cp = n;
+}
+
+#define smb_base(buf) ((byte *)(((byte *)(buf))+4))
+
 #else
 #error "Currently only on 386, sorry"
 #endif
@@ -96,6 +151,9 @@
 
 	unsigned long   size;	/* File size. */
 	unsigned short  access;	/* Access bits. */
+#if (ARCH == arm)
+	unsigned short  __unused;
+#endif
         unsigned long   f_pos;	/* File position. (For readdir.) */
 	char*           path;   /* Complete path, MS-DOS notation, with '\' */
 	int             len;	/* Namelength. */
diff -urNwbB linux/include/linux/tcp.h linux.arm/include/linux/tcp.h
--- linux/include/linux/tcp.h	Sun Mar  3 11:30:24 1996
+++ linux.arm/include/linux/tcp.h	Sun Feb 11 09:32:59 1996
@@ -50,7 +50,11 @@
 #error	"Adjust your <asm/byteorder.h> defines"
 #endif	
 	__u16	window;
+#ifndef __arm__
 	__u16	check;
+#else
+	__volatile__ __u16 check;
+#endif
 	__u16	urg_ptr;
 };
 
diff -urNwbB linux/include/linux/timex.h linux.arm/include/linux/timex.h
--- linux/include/linux/timex.h	Sun Mar  3 11:37:09 1996
+++ linux.arm/include/linux/timex.h	Sun Feb 11 09:33:00 1996
@@ -105,8 +105,13 @@
 #define PPS_VALID 120		/* pps signal watchdog max (s) */
 #define MAXGLITCH 30		/* pps signal glitch max (s) */
 
+#ifndef __arm__
 #define CLOCK_TICK_RATE	1193180 /* Underlying HZ */
 #define CLOCK_TICK_FACTOR	20	/* Factor of both 1000000 and CLOCK_TICK_RATE */
+#else
+#define CLOCK_TICK_RATE		2000000
+#define CLOCK_TICK_FACTOR	1000000
+#endif
 #define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ)	/* For divider */
 
 #define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
diff -urNwbB linux/include/linux/tqueue.h linux.arm/include/linux/tqueue.h
--- linux/include/linux/tqueue.h	Mon Jul 17 10:44:39 1995
+++ linux.arm/include/linux/tqueue.h	Sun Feb 11 09:33:00 1996
@@ -151,6 +151,7 @@
 			arg    = p -> data;
 			f      = p -> routine;
 			save_p = p -> next;
+			barrier (); /* rmk: needed to stop some over-zealous gccs */
 			p -> sync = 0;
 			(*f)(arg);
 			p = save_p;
diff -urNwbB linux/include/linux/udp.h linux.arm/include/linux/udp.h
--- linux/include/linux/udp.h	Wed Dec  1 12:44:15 1993
+++ linux.arm/include/linux/udp.h	Sun Feb 11 09:33:00 1996
@@ -22,7 +22,11 @@
   unsigned short	source;
   unsigned short	dest;
   unsigned short	len;
+#ifndef __arm__
   unsigned short	check;
+#else
+  __volatile__ unsigned short check;
+#endif
 };
 
 
diff -urNwbB linux/include/linux/vt.h linux.arm/include/linux/vt.h
--- linux/include/linux/vt.h	Sun Jun 18 16:11:38 1995
+++ linux.arm/include/linux/vt.h	Sun Feb 11 09:33:00 1996
@@ -49,4 +49,8 @@
 };
 #define VT_RESIZEX      0x560A  /* set kernel's idea of screensize + more */
 
+#ifdef __arm__
+#include <asm/vt.h>
+#endif
+
 #endif /* _LINUX_VT_H */
diff -urNwbB linux/init/main.c linux.arm/init/main.c
--- linux/init/main.c	Sun Mar  3 11:41:43 1996
+++ linux.arm/init/main.c	Sun Feb 11 13:05:06 1996
@@ -2,6 +2,9 @@
  *  linux/init/main.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Modified 11/02/96 by Russell King.
+ *	Added in expansion card initialisation for the ARM.
  */
 
 #define __KERNEL_SYSCALLS__
@@ -45,6 +48,9 @@
 extern void sock_init(void);
 extern long rd_init(long mem_start, int length);
 extern long pci_init(long, long);
+#ifdef CONFIG_ARM
+extern long ecard_init(long);
+#endif
 
 extern void swap_setup(char *str, int *ints);
 extern void buff_setup(char *str, int *ints);
@@ -516,6 +522,9 @@
 	memory_start = console_init(memory_start,memory_end);
 #ifdef CONFIG_PCI
 	memory_start = pci_init(memory_start,memory_end);
+#endif
+#ifdef CONFIG_ARM
+	memory_start = ecard_init(memory_start);
 #endif
 	memory_start = kmalloc_init(memory_start,memory_end);
 	sti();
diff -urNwbB linux/kernel/exit.c linux.arm/kernel/exit.c
--- linux/kernel/exit.c	Sun Mar  3 11:41:44 1996
+++ linux.arm/kernel/exit.c	Sun Feb 11 09:33:01 1996
@@ -4,6 +4,11 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+/*
+ * 08/07/1995	Russell King
+ *	for the arm, altered page alloc/free to kmalloc/kfree to reduce memory wastage.
+ */
+
 #undef DEBUG_PROC_TREE
 
 #include <linux/config.h>
@@ -97,7 +102,11 @@
 			release_thread(p);
 			if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page)
 				printk(KERN_ALERT "release: %s kernel stack corruption. Aiee\n", p->comm);
+#if !defined(__arm2__) && !defined(__arm3__)
 			free_page(p->kernel_stack_page);
+#else
+			kfree ((void *) p->kernel_stack_page);
+#endif
 			current->cmin_flt += p->min_flt + p->cmin_flt;
 			current->cmaj_flt += p->maj_flt + p->cmaj_flt;
 			current->cnswap += p->nswap + p->cnswap;
diff -urNwbB linux/kernel/fork.c linux.arm/kernel/fork.c
--- linux/kernel/fork.c	Sun Mar  3 11:41:44 1996
+++ linux.arm/kernel/fork.c	Sun Feb 11 09:33:02 1996
@@ -11,6 +11,11 @@
  * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()'
  */
 
+/*
+ * 08/07/1995	Russell King
+ *	for the arm, altered page alloc/free to kmalloc/kfree to reduce memory wastage.
+ */
+
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
@@ -205,7 +210,11 @@
 	p = (struct task_struct *) kmalloc(sizeof(*p), GFP_KERNEL);
 	if (!p)
 		goto bad_fork;
+#if !defined (__arm2__) && !defined (__arm3__)
 	new_stack = get_free_page(GFP_KERNEL);
+#else
+	new_stack = (unsigned long) kmalloc(4096, GFP_KERNEL);
+#endif
 	if (!new_stack)
 		goto bad_fork_free_p;
 	error = -EAGAIN;
@@ -284,7 +293,12 @@
 	REMOVE_LINKS(p);
 	nr_tasks--;
 bad_fork_free_stack:
+#if !defined (__arm2__) && !defined (__arm3__)
 	free_page(new_stack);
+#else
+	if (new_stack)
+		kfree ((void *) new_stack);
+#endif
 bad_fork_free_p:
 	kfree(p);
 bad_fork:
diff -urNwbB linux/kernel/ksyms.c linux.arm/kernel/ksyms.c
--- linux/kernel/ksyms.c	Sun Mar  3 11:41:44 1996
+++ linux.arm/kernel/ksyms.c	Sun Feb 11 09:33:02 1996
@@ -9,6 +9,10 @@
  * by Bjorn Ekwall <bj0rn@blox.se>
  */
 
+/*
+ * 08-07-1995	Russell King	Added extra symbols for ARM modules
+ */
+ 
 #include <linux/autoconf.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -95,6 +99,26 @@
 
 extern void (* iABI_hook)(struct pt_regs * regs);
 
+#ifdef __arm__
+#include <linux/string.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#define __need_memcpy_tofs
+#include <asm/segment.h>
+extern void inswb(unsigned int port, void *to, int len);
+extern void outswb(unsigned int port, const void *to, int len);
+extern void (*fp_save)(unsigned char *);
+extern void (*fp_restore)(unsigned char *);
+extern void fp_setup(void);
+extern void fpreturn(void);
+extern void fpundefinstr(void);
+extern void fp_enter(void);
+extern void fp_printk(void);
+extern struct task_struct *fp_current;
+extern void fp_send_sig(int);
+#endif
+
 struct symbol_table symbol_table = {
 #include <linux/symtab_begin.h>
 #ifdef CONFIG_MODVERSIONS
@@ -459,6 +483,76 @@
 	X(proc_unregister),
 	X(in_group_p),
 	X(generate_cluster),
+#endif
+
+#ifdef __arm__
+#define M(x) { (void *) & x, "_" # x }
+
+	/* floating point math emulator/helper support */
+	M(fpreturn),
+	M(fpundefinstr),
+	M(fp_enter),
+	M(fp_save),
+	M(fp_restore),
+	M(fp_setup),
+	M(fp_printk),
+	M(fp_current),
+	M(fp_send_sig),
+
+	/* string / mem functions */
+	M(strcpy),
+	M(strncpy),
+	M(strcat),
+	M(strncat),
+	M(strcmp),
+	M(strncmp),
+	M(strchr),
+	M(strlen),
+	M(strnlen),
+	M(strspn),
+	M(strpbrk),
+	M(strtok),
+/*	M(strchr),
+	M(strrchr),*/
+	M(memset),
+	M(memcpy),
+	M(memmove),
+	M(memcmp),
+	M(memscan),
+
+	/* io functions */
+	M(__inw),
+	M(__inb),
+	M(__outw),
+	M(__outb),
+	X(outswb),
+	X(inswb),
+
+	/* misc */
+	M(set_bit),
+	M(clear_bit),
+	M(test_bit),
+
+	/* expansion card support */
+	X(ecard_find),
+	X(ecard_readchunk),
+	X(udelay),
+
+	/* user mem (segment) */
+	M(put_user_long),
+	M(get_user_long),
+	M(put_user_word),
+	M(get_user_word),
+	M(put_user_byte),
+	M(get_user_byte),
+	M(memcpy_fromfs),
+	M(memcpy_tofs),
+	M(__memcpy_tofs),
+
+#ifdef CONFIG_SCSI
+	X(print_Scsi_Cmnd),
+	X(print_msg),
+#endif
 #endif
 	/********************************************************
 	 * Do not add anything below this line,
diff -urNwbB linux/kernel/ksyms.ver linux.arm/kernel/ksyms.ver
--- linux/kernel/ksyms.ver	Sun Mar  3 11:30:27 1996
+++ linux.arm/kernel/ksyms.ver	Sun Feb 25 13:07:16 1996
@@ -3,85 +3,92 @@
 #ifndef _KSYMS_VER_
 #define _KSYMS_VER_
 #define rename_module_symbol	_set_ver(rename_module_symbol, b81c73c1)
-#define register_symtab	_set_ver(register_symtab, e910ea66)
-#define EISA_bus	_set_ver(EISA_bus, 7e37737c)
-#define wp_works_ok	_set_ver(wp_works_ok, f37f99e9)
+#define register_symtab	_set_ver(register_symtab, 19fab810)
+#define get_options	_set_ver(get_options, 9189b3cf)
 #define verify_area	_set_ver(verify_area, 4cfda560)
-#define do_mmap	_set_ver(do_mmap, 677e7ee1)
+#define do_mmap	_set_ver(do_mmap, f5e1bbc5)
 #define do_munmap	_set_ver(do_munmap, 6221f117)
 #define zeromap_page_range	_set_ver(zeromap_page_range, 7c395a26)
 #define unmap_page_range	_set_ver(unmap_page_range, 0110085f)
-#define insert_vm_struct	_set_ver(insert_vm_struct, 1f4e4882)
-#define merge_segments	_set_ver(merge_segments, 6854be5a)
-#define __get_free_pages	_set_ver(__get_free_pages, 5243d78b)
+#define insert_vm_struct	_set_ver(insert_vm_struct, 7f6c7aeb)
+#define merge_segments	_set_ver(merge_segments, 7a4e5f90)
+#define __get_free_pages	_set_ver(__get_free_pages, 5b8fa215)
 #define free_pages	_set_ver(free_pages, 96448859)
 #define kmalloc	_set_ver(kmalloc, d31fb2cb)
-#define kfree_s	_set_ver(kfree_s, 1e72eb79)
+#define kfree	_set_ver(kfree, 801a0af7)
 #define vmalloc	_set_ver(vmalloc, 667f3e25)
+#define vremap	_set_ver(vremap, 2e578c28)
 #define vfree	_set_ver(vfree, 6df52add)
+#define mem_map	_set_ver(mem_map, 94646963)
+#define remap_page_range	_set_ver(remap_page_range, 26ed0347)
 #define getname	_set_ver(getname, 81487159)
 #define putname	_set_ver(putname, b19e8126)
-#define __iget	_set_ver(__iget, ee2b6320)
-#define iput	_set_ver(iput, 59241ced)
-#define namei	_set_ver(namei, 00478bcd)
-#define lnamei	_set_ver(lnamei, fcfddbb1)
-#define open_namei	_set_ver(open_namei, 414b2b0f)
-#define close_fp	_set_ver(close_fp, 1d4c15d8)
-#define check_disk_change	_set_ver(check_disk_change, b66ed457)
-#define invalidate_buffers	_set_ver(invalidate_buffers, c65255f1)
-#define fsync_dev	_set_ver(fsync_dev, a221190d)
-#define permission	_set_ver(permission, 0ebf7474)
-#define inode_setattr	_set_ver(inode_setattr, 0c80a3c1)
-#define inode_change_ok	_set_ver(inode_change_ok, 5d1cb326)
-#define generic_mmap	_set_ver(generic_mmap, d4ff59f3)
-#define set_blocksize	_set_ver(set_blocksize, f45fda38)
-#define getblk	_set_ver(getblk, d40228ac)
-#define bread	_set_ver(bread, c73bf0f0)
-#define breada	_set_ver(breada, eb8e858c)
-#define brelse	_set_ver(brelse, 4c27ac3d)
-#define ll_rw_block	_set_ver(ll_rw_block, f3aa4dd3)
-#define __wait_on_buffer	_set_ver(__wait_on_buffer, e8fcc968)
-#define dcache_lookup	_set_ver(dcache_lookup, 83336566)
-#define dcache_add	_set_ver(dcache_add, fe71f11e)
-#define register_chrdev	_set_ver(register_chrdev, da99513f)
+#define __iget	_set_ver(__iget, 40da7c36)
+#define iput	_set_ver(iput, c9efcf6e)
+#define namei	_set_ver(namei, 01ce3d8f)
+#define lnamei	_set_ver(lnamei, e832164f)
+#define open_namei	_set_ver(open_namei, bfab0c23)
+#define close_fp	_set_ver(close_fp, 9f637f8e)
+#define check_disk_change	_set_ver(check_disk_change, 99b9282b)
+#define invalidate_buffers	_set_ver(invalidate_buffers, a67c1277)
+#define invalidate_inodes	_set_ver(invalidate_inodes, d98fd8ed)
+#define fsync_dev	_set_ver(fsync_dev, 1213df0c)
+#define permission	_set_ver(permission, c35f4702)
+#define inode_setattr	_set_ver(inode_setattr, 052e6416)
+#define inode_change_ok	_set_ver(inode_change_ok, d4ccd406)
+#define generic_mmap	_set_ver(generic_mmap, abda2851)
+#define set_blocksize	_set_ver(set_blocksize, e8909872)
+#define getblk	_set_ver(getblk, ade9e357)
+#define bread	_set_ver(bread, 270b375f)
+#define breada	_set_ver(breada, b8e9462b)
+#define brelse	_set_ver(brelse, c2f3c746)
+#define ll_rw_block	_set_ver(ll_rw_block, fe36073b)
+#define __wait_on_buffer	_set_ver(__wait_on_buffer, 07535ad9)
+#define dcache_lookup	_set_ver(dcache_lookup, dea852f3)
+#define dcache_add	_set_ver(dcache_add, 9572fca8)
+#define aout_core_dump	_set_ver(aout_core_dump, 0ccff24b)
+#define register_chrdev	_set_ver(register_chrdev, 030f0b50)
 #define unregister_chrdev	_set_ver(unregister_chrdev, 61ea5ee8)
-#define register_blkdev	_set_ver(register_blkdev, 4699a621)
+#define register_blkdev	_set_ver(register_blkdev, 2244b57a)
 #define unregister_blkdev	_set_ver(unregister_blkdev, d39bbca9)
-#define tty_register_driver	_set_ver(tty_register_driver, fcc8591c)
-#define tty_unregister_driver	_set_ver(tty_unregister_driver, c78132a8)
-#define tty_std_termios	_set_ver(tty_std_termios, cf350678)
-#define block_read	_set_ver(block_read, a7fe4f51)
-#define block_write	_set_ver(block_write, 902674c9)
-#define block_fsync	_set_ver(block_fsync, 182888d8)
-#define wait_for_request	_set_ver(wait_for_request, 9ca2932e)
+#define tty_register_driver	_set_ver(tty_register_driver, 7b4e91f9)
+#define tty_unregister_driver	_set_ver(tty_unregister_driver, 08578e24)
+#define tty_std_termios	_set_ver(tty_std_termios, 7dc7ba14)
+#define block_read	_set_ver(block_read, 051dcd42)
+#define block_write	_set_ver(block_write, a01a5f45)
+#define block_fsync	_set_ver(block_fsync, 692e8d51)
+#define wait_for_request	_set_ver(wait_for_request, 54c7aae7)
 #define blksize_size	_set_ver(blksize_size, dea1eb55)
 #define hardsect_size	_set_ver(hardsect_size, ed1ee14f)
 #define blk_size	_set_ver(blk_size, f60b5398)
-#define blk_dev	_set_ver(blk_dev, dbf5fdd4)
-#define is_read_only	_set_ver(is_read_only, b0c5f83e)
+#define blk_dev	_set_ver(blk_dev, abebf007)
+#define is_read_only	_set_ver(is_read_only, 588904b7)
 #define set_device_ro	_set_ver(set_device_ro, 8fb69e13)
-#define bmap	_set_ver(bmap, 73bb8bdd)
-#define sync_dev	_set_ver(sync_dev, 9bca536d)
-#define get_blkfops	_set_ver(get_blkfops, 83827791)
+#define bmap	_set_ver(bmap, 0a0ad8c5)
+#define sync_dev	_set_ver(sync_dev, 5f98551e)
+#define get_blkfops	_set_ver(get_blkfops, ea2b23c0)
 #define register_serial	_set_ver(register_serial, 3425f38c)
 #define unregister_serial	_set_ver(unregister_serial, c013d717)
-#define tty_hangup	_set_ver(tty_hangup, e3487df0)
-#define tty_wait_until_sent	_set_ver(tty_wait_until_sent, da85d428)
-#define tty_check_change	_set_ver(tty_check_change, 705eaab0)
-#define tty_hung_up_p	_set_ver(tty_hung_up_p, f99ac1e4)
-#define register_filesystem	_set_ver(register_filesystem, 1c7110ef)
-#define unregister_filesystem	_set_ver(unregister_filesystem, 5e353af7)
-#define register_binfmt	_set_ver(register_binfmt, 66ece706)
-#define unregister_binfmt	_set_ver(unregister_binfmt, 41822618)
-#define lookup_exec_domain	_set_ver(lookup_exec_domain, 32f10d48)
-#define register_exec_domain	_set_ver(register_exec_domain, eda4711f)
-#define unregister_exec_domain	_set_ver(unregister_exec_domain, 78ea447c)
-#define request_irq	_set_ver(request_irq, 9e81629c)
+#define tty_hangup	_set_ver(tty_hangup, af68e585)
+#define tty_wait_until_sent	_set_ver(tty_wait_until_sent, 8094f69c)
+#define tty_check_change	_set_ver(tty_check_change, c96d976e)
+#define tty_hung_up_p	_set_ver(tty_hung_up_p, 813cecdc)
+#define register_filesystem	_set_ver(register_filesystem, ad496fcf)
+#define unregister_filesystem	_set_ver(unregister_filesystem, 879db926)
+#define register_binfmt	_set_ver(register_binfmt, e3ce227c)
+#define unregister_binfmt	_set_ver(unregister_binfmt, f0ed2f66)
+#define lookup_exec_domain	_set_ver(lookup_exec_domain, 403537c9)
+#define register_exec_domain	_set_ver(register_exec_domain, f883c357)
+#define unregister_exec_domain	_set_ver(unregister_exec_domain, c8fe8028)
+#define request_irq	_set_ver(request_irq, d95d460a)
 #define free_irq	_set_ver(free_irq, f487dc0c)
 #define enable_irq	_set_ver(enable_irq, 54e09f5f)
 #define disable_irq	_set_ver(disable_irq, b4449c1f)
+#define probe_irq_on	_set_ver(probe_irq_on, 1c163c9d)
+#define probe_irq_off	_set_ver(probe_irq_off, 065701b6)
 #define bh_active	_set_ver(bh_active, 98fb5ca1)
 #define bh_mask	_set_ver(bh_mask, 1abf3d3f)
+#define bh_base	_set_ver(bh_base, 0197e29a)
 #define add_timer	_set_ver(add_timer, f13cb728)
 #define del_timer	_set_ver(del_timer, c7aff713)
 #define tq_timer	_set_ver(tq_timer, 46cf583e)
@@ -90,21 +97,22 @@
 #define tq_last	_set_ver(tq_last, 457cf547)
 #define timer_active	_set_ver(timer_active, 5a6747ee)
 #define timer_table	_set_ver(timer_table, 9e03b650)
+#define intr_count	_set_ver(intr_count, aaedaa28)
+#define autoirq_setup	_set_ver(autoirq_setup, 22013cb8)
+#define autoirq_report	_set_ver(autoirq_report, 5edb09ac)
 #define request_dma	_set_ver(request_dma, 2a687646)
 #define free_dma	_set_ver(free_dma, 5d4b914c)
-#define disable_hlt	_set_ver(disable_hlt, 794487ee)
-#define enable_hlt	_set_ver(enable_hlt, 9c7077bd)
 #define check_region	_set_ver(check_region, b91154fb)
 #define request_region	_set_ver(request_region, 138b0a1e)
 #define release_region	_set_ver(release_region, f41d6d31)
-#define wake_up	_set_ver(wake_up, e8d71419)
-#define wake_up_interruptible	_set_ver(wake_up_interruptible, 64c8cb92)
-#define sleep_on	_set_ver(sleep_on, 67a00cee)
-#define interruptible_sleep_on	_set_ver(interruptible_sleep_on, 6a5fc80d)
+#define wake_up	_set_ver(wake_up, ac6a33c8)
+#define wake_up_interruptible	_set_ver(wake_up_interruptible, 003286e7)
+#define sleep_on	_set_ver(sleep_on, 0a670ca8)
+#define interruptible_sleep_on	_set_ver(interruptible_sleep_on, 8c777f73)
 #define schedule	_set_ver(schedule, 01000e51)
-#define current	_set_ver(current, fc1cb29b)
+#define current_set	_set_ver(current_set, a7f38d3d)
 #define jiffies	_set_ver(jiffies, 2f7c7437)
-#define xtime	_set_ver(xtime, e70c0be0)
+#define xtime	_set_ver(xtime, ea6d79fb)
 #define loops_per_sec	_set_ver(loops_per_sec, 40a14192)
 #define need_resched	_set_ver(need_resched, dfc016ea)
 #define kill_proc	_set_ver(kill_proc, 911f760a)
@@ -114,275 +122,135 @@
 #define printk	_set_ver(printk, ad1148ba)
 #define sprintf	_set_ver(sprintf, f9003107)
 #define vsprintf	_set_ver(vsprintf, e605cb6b)
+#define kdevname	_set_ver(kdevname, d173c26c)
 #define simple_strtoul	_set_ver(simple_strtoul, bdb8c1e3)
 #define system_utsname	_set_ver(system_utsname, 066845bc)
 #define sys_call_table	_set_ver(sys_call_table, 79fa4011)
-#define do_signal	_set_ver(do_signal, 86f9bc59)
-#define send_sig	_set_ver(send_sig, 5cddd8d9)
+#define send_sig	_set_ver(send_sig, 6a772f11)
 #define setup_arg_pages	_set_ver(setup_arg_pages, fe68d94a)
 #define copy_strings	_set_ver(copy_strings, 232aee96)
-#define create_tables	_set_ver(create_tables, ba788fa2)
-#define do_execve	_set_ver(do_execve, 8c99dc0a)
-#define flush_old_exec	_set_ver(flush_old_exec, c737e178)
-#define open_inode	_set_ver(open_inode, 27302cb6)
-#define read_exec	_set_ver(read_exec, a80a2dd0)
+#define create_tables	_set_ver(create_tables, 88916da7)
+#define do_execve	_set_ver(do_execve, 945e29fb)
+#define flush_old_exec	_set_ver(flush_old_exec, 73af8ebc)
+#define open_inode	_set_ver(open_inode, ead83ee5)
+#define read_exec	_set_ver(read_exec, eaa2d89e)
 #define si_meminfo	_set_ver(si_meminfo, bb05fc9a)
-#define sock_register	_set_ver(sock_register, d68e1649)
+#define sock_register	_set_ver(sock_register, 6feca076)
 #define sock_unregister	_set_ver(sock_unregister, 72c332bd)
-#define inet_add_protocol	_set_ver(inet_add_protocol, 55292121)
-#define inet_del_protocol	_set_ver(inet_del_protocol, 73908a1b)
-#define slhc_init	_set_ver(slhc_init, e490a4b8)
-#define slhc_free	_set_ver(slhc_free, 39ab902b)
-#define slhc_remember	_set_ver(slhc_remember, db333be6)
-#define slhc_compress	_set_ver(slhc_compress, e753e2d2)
-#define slhc_uncompress	_set_ver(slhc_uncompress, 81cc1144)
+#define inet_add_protocol	_set_ver(inet_add_protocol, 708ce6e6)
+#define inet_del_protocol	_set_ver(inet_del_protocol, b80632fd)
+#define rarp_ioctl_hook	_set_ver(rarp_ioctl_hook, 0b5752f0)
+#define init_etherdev	_set_ver(init_etherdev, ee6b3752)
+#define ip_rt_route	_set_ver(ip_rt_route, bb5d6493)
+#define arp_send	_set_ver(arp_send, 67037f81)
+#define ip_forward	_set_ver(ip_forward, f26506a8)
+#define slhc_init	_set_ver(slhc_init, 50a6b58e)
+#define slhc_free	_set_ver(slhc_free, c0371e81)
+#define slhc_remember	_set_ver(slhc_remember, dca1fdb4)
+#define slhc_compress	_set_ver(slhc_compress, 9349e4f9)
+#define slhc_uncompress	_set_ver(slhc_uncompress, f8e2bfb9)
+#define slhc_toss	_set_ver(slhc_toss, 3d3b2bd7)
 #define register_netdevice_notifier	_set_ver(register_netdevice_notifier, e7aace7c)
 #define unregister_netdevice_notifier	_set_ver(unregister_netdevice_notifier, be114416)
-#define floppy_track_buffer	_set_ver(floppy_track_buffer, c6e3f7c2)
-#define register_netdev	_set_ver(register_netdev, 0d8d1bb4)
-#define unregister_netdev	_set_ver(unregister_netdev, 25a99579)
-#define ether_setup	_set_ver(ether_setup, 4eafef91)
-#define alloc_skb	_set_ver(alloc_skb, b6b523ba)
-#define kfree_skb	_set_ver(kfree_skb, 0b938572)
-#define dev_kfree_skb	_set_ver(dev_kfree_skb, aa1fe7f4)
-#define netif_rx	_set_ver(netif_rx, d8051cb2)
-#define dev_rint	_set_ver(dev_rint, 040d3f4b)
-#define dev_tint	_set_ver(dev_tint, 860b350b)
+#define register_netdev	_set_ver(register_netdev, 81f92702)
+#define unregister_netdev	_set_ver(unregister_netdev, aa68d4de)
+#define ether_setup	_set_ver(ether_setup, 6d1b1492)
+#define eth_type_trans	_set_ver(eth_type_trans, eeaf2704)
+#define eth_copy_and_sum	_set_ver(eth_copy_and_sum, ef4ddc3a)
+#define alloc_skb	_set_ver(alloc_skb, 577c3c78)
+#define kfree_skb	_set_ver(kfree_skb, dd3232e5)
+#define dev_alloc_skb	_set_ver(dev_alloc_skb, 103603a7)
+#define dev_kfree_skb	_set_ver(dev_kfree_skb, 8f7bdfbe)
+#define netif_rx	_set_ver(netif_rx, 8c58e94c)
+#define dev_rint	_set_ver(dev_rint, d4a4b9db)
+#define dev_tint	_set_ver(dev_tint, 0bbc05da)
 #define irq2dev_map	_set_ver(irq2dev_map, 10bdcd8a)
-#define dev_add_pack	_set_ver(dev_add_pack, 6d7d9be4)
-#define dev_remove_pack	_set_ver(dev_remove_pack, 784fa59f)
-#define dev_get	_set_ver(dev_get, 72ed90fd)
+#define dev_add_pack	_set_ver(dev_add_pack, 8a658bd0)
+#define dev_remove_pack	_set_ver(dev_remove_pack, da5c9b2f)
+#define dev_get	_set_ver(dev_get, 05b00074)
 #define dev_ioctl	_set_ver(dev_ioctl, 08760203)
-#define dev_queue_xmit	_set_ver(dev_queue_xmit, 4a478225)
-#define dev_base	_set_ver(dev_base, 0a8809f0)
-#define dev_close	_set_ver(dev_close, 9bdad56d)
-#define arp_find	_set_ver(arp_find, a141bd11)
-#define n_tty_ioctl	_set_ver(n_tty_ioctl, 538e5fa6)
-#define tty_register_ldisc	_set_ver(tty_register_ldisc, 8fdde939)
-#define kill_fasync	_set_ver(kill_fasync, 890501b6)
+#define dev_queue_xmit	_set_ver(dev_queue_xmit, 7fce9094)
+#define dev_base	_set_ver(dev_base, 80e6f201)
+#define dev_close	_set_ver(dev_close, 18af801d)
+#define arp_find	_set_ver(arp_find, 1ca163e2)
+#define n_tty_ioctl	_set_ver(n_tty_ioctl, 8dd15cd4)
+#define tty_register_ldisc	_set_ver(tty_register_ldisc, e58ba568)
+#define kill_fasync	_set_ver(kill_fasync, 39a5cd3d)
 #define in_scan_scsis	_set_ver(in_scan_scsis, 21874a88)
 #define scsi_register_module	_set_ver(scsi_register_module, 8eff1010)
 #define scsi_unregister_module	_set_ver(scsi_unregister_module, d913b8f0)
 #define scsi_free	_set_ver(scsi_free, 475dddfa)
 #define scsi_malloc	_set_ver(scsi_malloc, 1cce3f92)
-#define scsi_register	_set_ver(scsi_register, d6e77069)
-#define scsi_unregister	_set_ver(scsi_unregister, 3b0b616b)
-#define scsicam_bios_param	_set_ver(scsicam_bios_param, 3d965248)
+#define scsi_register	_set_ver(scsi_register, d02c37d5)
+#define scsi_unregister	_set_ver(scsi_unregister, 9810005e)
+#define scsicam_bios_param	_set_ver(scsicam_bios_param, 59bee323)
+#define allocate_device	_set_ver(allocate_device, 83738353)
+#define scsi_do_cmd	_set_ver(scsi_do_cmd, bb2113c0)
+#define scsi_command_size	_set_ver(scsi_command_size, 7db01421)
 #define scsi_init_malloc	_set_ver(scsi_init_malloc, e5167cbc)
 #define scsi_init_free	_set_ver(scsi_init_free, 8b2721f8)
+#define scsi_ioctl	_set_ver(scsi_ioctl, 12daead8)
 #define print_command	_set_ver(print_command, 6f14cd75)
 #define print_msg	_set_ver(print_msg, 0465f877)
 #define print_status	_set_ver(print_status, 32f84646)
-#define set_writetime	_set_ver(set_writetime, 52131916)
+#define print_sense	_set_ver(print_sense, cdc49c79)
+#define dma_free_sectors	_set_ver(dma_free_sectors, a3218d12)
+#define kernel_scsi_ioctl	_set_ver(kernel_scsi_ioctl, 5e3d91f0)
+#define need_isa_buffer	_set_ver(need_isa_buffer, 2cb052fc)
+#define request_queueable	_set_ver(request_queueable, 03913bb5)
+#define dispatch_scsi_info_ptr	_set_ver(dispatch_scsi_info_ptr, 47c217d7)
+#define generic_proc_info	_set_ver(generic_proc_info, c41091e5)
+#define scsi_devices	_set_ver(scsi_devices, 63ec9c06)
+#define gendisk_head	_set_ver(gendisk_head, ae2b4d91)
+#define resetup_one_dev	_set_ver(resetup_one_dev, 97f0bc9d)
+#define proc_print_scsidevice	_set_ver(proc_print_scsidevice, 95e93b69)
+#define set_writetime	_set_ver(set_writetime, d0c03732)
 #define sys_tz	_set_ver(sys_tz, aa3c9782)
-#define __wait_on_super	_set_ver(__wait_on_super, 61a5c00a)
-#define file_fsync	_set_ver(file_fsync, d30a190f)
-#define clear_inode	_set_ver(clear_inode, da2b0e9f)
-#define refile_buffer	_set_ver(refile_buffer, 8c69e123)
+#define __wait_on_super	_set_ver(__wait_on_super, ab0dcfa2)
+#define file_fsync	_set_ver(file_fsync, 600edbd8)
+#define clear_inode	_set_ver(clear_inode, 592343b2)
+#define refile_buffer	_set_ver(refile_buffer, 4dd86604)
 #define ___strtok	_set_ver(___strtok, 8b55d69c)
-#define init_fifo	_set_ver(init_fifo, 082629c7)
-#define super_blocks	_set_ver(super_blocks, e1f1ee99)
-#define chrdev_inode_operations	_set_ver(chrdev_inode_operations, 6ba1faa3)
-#define blkdev_inode_operations	_set_ver(blkdev_inode_operations, ed443696)
+#define init_fifo	_set_ver(init_fifo, 63763043)
+#define super_blocks	_set_ver(super_blocks, dd0763de)
+#define chrdev_inode_operations	_set_ver(chrdev_inode_operations, 5a3bf3f3)
+#define blkdev_inode_operations	_set_ver(blkdev_inode_operations, dcde3fc6)
 #define read_ahead	_set_ver(read_ahead, bbcd3768)
-#define get_hash_table	_set_ver(get_hash_table, 3b5f3c55)
-#define get_empty_inode	_set_ver(get_empty_inode, 554bdc75)
-#define insert_inode_hash	_set_ver(insert_inode_hash, 59b8c371)
+#define get_hash_table	_set_ver(get_hash_table, e26ffc88)
+#define get_empty_inode	_set_ver(get_empty_inode, a6787725)
+#define insert_inode_hash	_set_ver(insert_inode_hash, d4041b38)
 #define event	_set_ver(event, a6aac9c1)
-#define __down	_set_ver(__down, 75aa9e96)
-#endif /* _KSYMS_VER_ */
-#endif /* CONFIG_MODVERSIONS !__GENKSYMS__ */
-/**** This file is generated by genksyms  DO NOT EDIT! ****/
-#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__)
-#ifndef _KSYMS_VER_
-#define _KSYMS_VER_
-#define rename_module_symbol	_set_ver(rename_module_symbol, b81c73c1)
-#define register_symtab	_set_ver(register_symtab, e910ea66)
-#define EISA_bus	_set_ver(EISA_bus, 7e37737c)
-#define wp_works_ok	_set_ver(wp_works_ok, f37f99e9)
-#define verify_area	_set_ver(verify_area, 4cfda560)
-#define do_mmap	_set_ver(do_mmap, 677e7ee1)
-#define do_munmap	_set_ver(do_munmap, 6221f117)
-#define zeromap_page_range	_set_ver(zeromap_page_range, 7c395a26)
-#define unmap_page_range	_set_ver(unmap_page_range, 0110085f)
-#define insert_vm_struct	_set_ver(insert_vm_struct, 1f4e4882)
-#define merge_segments	_set_ver(merge_segments, 6854be5a)
-#define __get_free_pages	_set_ver(__get_free_pages, 5243d78b)
-#define free_pages	_set_ver(free_pages, 96448859)
-#define kmalloc	_set_ver(kmalloc, d31fb2cb)
-#define kfree_s	_set_ver(kfree_s, 1e72eb79)
-#define vmalloc	_set_ver(vmalloc, 667f3e25)
-#define vfree	_set_ver(vfree, 6df52add)
-#define getname	_set_ver(getname, 81487159)
-#define putname	_set_ver(putname, b19e8126)
-#define __iget	_set_ver(__iget, ee2b6320)
-#define iput	_set_ver(iput, 59241ced)
-#define namei	_set_ver(namei, 00478bcd)
-#define lnamei	_set_ver(lnamei, fcfddbb1)
-#define open_namei	_set_ver(open_namei, 414b2b0f)
-#define close_fp	_set_ver(close_fp, 1d4c15d8)
-#define check_disk_change	_set_ver(check_disk_change, b66ed457)
-#define invalidate_buffers	_set_ver(invalidate_buffers, c65255f1)
-#define fsync_dev	_set_ver(fsync_dev, a221190d)
-#define permission	_set_ver(permission, 0ebf7474)
-#define inode_setattr	_set_ver(inode_setattr, 0c80a3c1)
-#define inode_change_ok	_set_ver(inode_change_ok, 5d1cb326)
-#define generic_mmap	_set_ver(generic_mmap, d4ff59f3)
-#define set_blocksize	_set_ver(set_blocksize, f45fda38)
-#define getblk	_set_ver(getblk, d40228ac)
-#define bread	_set_ver(bread, c73bf0f0)
-#define breada	_set_ver(breada, eb8e858c)
-#define brelse	_set_ver(brelse, 4c27ac3d)
-#define ll_rw_block	_set_ver(ll_rw_block, f3aa4dd3)
-#define __wait_on_buffer	_set_ver(__wait_on_buffer, e8fcc968)
-#define dcache_lookup	_set_ver(dcache_lookup, 83336566)
-#define dcache_add	_set_ver(dcache_add, fe71f11e)
-#define register_chrdev	_set_ver(register_chrdev, da99513f)
-#define unregister_chrdev	_set_ver(unregister_chrdev, 61ea5ee8)
-#define register_blkdev	_set_ver(register_blkdev, 4699a621)
-#define unregister_blkdev	_set_ver(unregister_blkdev, d39bbca9)
-#define tty_register_driver	_set_ver(tty_register_driver, fcc8591c)
-#define tty_unregister_driver	_set_ver(tty_unregister_driver, c78132a8)
-#define tty_std_termios	_set_ver(tty_std_termios, cf350678)
-#define block_read	_set_ver(block_read, a7fe4f51)
-#define block_write	_set_ver(block_write, 902674c9)
-#define block_fsync	_set_ver(block_fsync, 182888d8)
-#define wait_for_request	_set_ver(wait_for_request, 9ca2932e)
-#define blksize_size	_set_ver(blksize_size, dea1eb55)
-#define hardsect_size	_set_ver(hardsect_size, ed1ee14f)
-#define blk_size	_set_ver(blk_size, f60b5398)
-#define blk_dev	_set_ver(blk_dev, dbf5fdd4)
-#define is_read_only	_set_ver(is_read_only, b0c5f83e)
-#define set_device_ro	_set_ver(set_device_ro, 8fb69e13)
-#define bmap	_set_ver(bmap, 73bb8bdd)
-#define sync_dev	_set_ver(sync_dev, 9bca536d)
-#define get_blkfops	_set_ver(get_blkfops, 83827791)
-#define register_serial	_set_ver(register_serial, 3425f38c)
-#define unregister_serial	_set_ver(unregister_serial, c013d717)
-#define tty_hangup	_set_ver(tty_hangup, e3487df0)
-#define tty_wait_until_sent	_set_ver(tty_wait_until_sent, da85d428)
-#define tty_check_change	_set_ver(tty_check_change, 705eaab0)
-#define tty_hung_up_p	_set_ver(tty_hung_up_p, f99ac1e4)
-#define register_filesystem	_set_ver(register_filesystem, 1c7110ef)
-#define unregister_filesystem	_set_ver(unregister_filesystem, 5e353af7)
-#define register_binfmt	_set_ver(register_binfmt, 66ece706)
-#define unregister_binfmt	_set_ver(unregister_binfmt, 41822618)
-#define lookup_exec_domain	_set_ver(lookup_exec_domain, 32f10d48)
-#define register_exec_domain	_set_ver(register_exec_domain, eda4711f)
-#define unregister_exec_domain	_set_ver(unregister_exec_domain, 78ea447c)
-#define request_irq	_set_ver(request_irq, 9e81629c)
-#define free_irq	_set_ver(free_irq, f487dc0c)
-#define enable_irq	_set_ver(enable_irq, 54e09f5f)
-#define disable_irq	_set_ver(disable_irq, b4449c1f)
-#define bh_active	_set_ver(bh_active, 98fb5ca1)
-#define bh_mask	_set_ver(bh_mask, 1abf3d3f)
-#define add_timer	_set_ver(add_timer, f13cb728)
-#define del_timer	_set_ver(del_timer, c7aff713)
-#define tq_timer	_set_ver(tq_timer, 46cf583e)
-#define tq_immediate	_set_ver(tq_immediate, 46cf583e)
-#define tq_scheduler	_set_ver(tq_scheduler, 46cf583e)
-#define tq_last	_set_ver(tq_last, 457cf547)
-#define timer_active	_set_ver(timer_active, 5a6747ee)
-#define timer_table	_set_ver(timer_table, 9e03b650)
-#define request_dma	_set_ver(request_dma, 2a687646)
-#define free_dma	_set_ver(free_dma, 5d4b914c)
-#define disable_hlt	_set_ver(disable_hlt, 794487ee)
-#define enable_hlt	_set_ver(enable_hlt, 9c7077bd)
-#define check_region	_set_ver(check_region, b91154fb)
-#define request_region	_set_ver(request_region, 138b0a1e)
-#define release_region	_set_ver(release_region, f41d6d31)
-#define wake_up	_set_ver(wake_up, e8d71419)
-#define wake_up_interruptible	_set_ver(wake_up_interruptible, 64c8cb92)
-#define sleep_on	_set_ver(sleep_on, 67a00cee)
-#define interruptible_sleep_on	_set_ver(interruptible_sleep_on, 6a5fc80d)
-#define schedule	_set_ver(schedule, 01000e51)
-#define current	_set_ver(current, fc1cb29b)
-#define jiffies	_set_ver(jiffies, 2f7c7437)
-#define xtime	_set_ver(xtime, e70c0be0)
-#define loops_per_sec	_set_ver(loops_per_sec, 40a14192)
-#define need_resched	_set_ver(need_resched, dfc016ea)
-#define kill_proc	_set_ver(kill_proc, 911f760a)
-#define kill_pg	_set_ver(kill_pg, 0a758a45)
-#define kill_sl	_set_ver(kill_sl, 49625e94)
-#define panic	_set_ver(panic, 400c0de3)
-#define printk	_set_ver(printk, ad1148ba)
-#define sprintf	_set_ver(sprintf, f9003107)
-#define vsprintf	_set_ver(vsprintf, e605cb6b)
-#define simple_strtoul	_set_ver(simple_strtoul, bdb8c1e3)
-#define system_utsname	_set_ver(system_utsname, 066845bc)
-#define sys_call_table	_set_ver(sys_call_table, 79fa4011)
-#define do_signal	_set_ver(do_signal, 86f9bc59)
-#define send_sig	_set_ver(send_sig, 5cddd8d9)
-#define setup_arg_pages	_set_ver(setup_arg_pages, fe68d94a)
-#define copy_strings	_set_ver(copy_strings, 232aee96)
-#define create_tables	_set_ver(create_tables, ba788fa2)
-#define do_execve	_set_ver(do_execve, 8c99dc0a)
-#define flush_old_exec	_set_ver(flush_old_exec, c737e178)
-#define open_inode	_set_ver(open_inode, 27302cb6)
-#define read_exec	_set_ver(read_exec, a80a2dd0)
-#define si_meminfo	_set_ver(si_meminfo, bb05fc9a)
-#define sock_register	_set_ver(sock_register, d68e1649)
-#define sock_unregister	_set_ver(sock_unregister, 72c332bd)
-#define inet_add_protocol	_set_ver(inet_add_protocol, 55292121)
-#define inet_del_protocol	_set_ver(inet_del_protocol, 73908a1b)
-#define slhc_init	_set_ver(slhc_init, e490a4b8)
-#define slhc_free	_set_ver(slhc_free, 39ab902b)
-#define slhc_remember	_set_ver(slhc_remember, db333be6)
-#define slhc_compress	_set_ver(slhc_compress, e753e2d2)
-#define slhc_uncompress	_set_ver(slhc_uncompress, 81cc1144)
-#define register_netdevice_notifier	_set_ver(register_netdevice_notifier, e7aace7c)
-#define unregister_netdevice_notifier	_set_ver(unregister_netdevice_notifier, be114416)
-#define floppy_track_buffer	_set_ver(floppy_track_buffer, c6e3f7c2)
-#define register_netdev	_set_ver(register_netdev, 0d8d1bb4)
-#define unregister_netdev	_set_ver(unregister_netdev, 25a99579)
-#define ether_setup	_set_ver(ether_setup, 4eafef91)
-#define alloc_skb	_set_ver(alloc_skb, b6b523ba)
-#define kfree_skb	_set_ver(kfree_skb, 0b938572)
-#define dev_kfree_skb	_set_ver(dev_kfree_skb, aa1fe7f4)
-#define netif_rx	_set_ver(netif_rx, d8051cb2)
-#define dev_rint	_set_ver(dev_rint, 040d3f4b)
-#define dev_tint	_set_ver(dev_tint, 860b350b)
-#define irq2dev_map	_set_ver(irq2dev_map, 10bdcd8a)
-#define dev_add_pack	_set_ver(dev_add_pack, 6d7d9be4)
-#define dev_remove_pack	_set_ver(dev_remove_pack, 784fa59f)
-#define dev_get	_set_ver(dev_get, 72ed90fd)
-#define dev_ioctl	_set_ver(dev_ioctl, 08760203)
-#define dev_queue_xmit	_set_ver(dev_queue_xmit, 4a478225)
-#define dev_base	_set_ver(dev_base, 0a8809f0)
-#define dev_close	_set_ver(dev_close, 9bdad56d)
-#define arp_find	_set_ver(arp_find, a141bd11)
-#define n_tty_ioctl	_set_ver(n_tty_ioctl, 538e5fa6)
-#define tty_register_ldisc	_set_ver(tty_register_ldisc, 8fdde939)
-#define kill_fasync	_set_ver(kill_fasync, 890501b6)
-#define in_scan_scsis	_set_ver(in_scan_scsis, 21874a88)
-#define scsi_register_module	_set_ver(scsi_register_module, 8eff1010)
-#define scsi_unregister_module	_set_ver(scsi_unregister_module, d913b8f0)
-#define scsi_free	_set_ver(scsi_free, 475dddfa)
-#define scsi_malloc	_set_ver(scsi_malloc, 1cce3f92)
-#define scsi_register	_set_ver(scsi_register, d6e77069)
-#define scsi_unregister	_set_ver(scsi_unregister, 3b0b616b)
-#define scsicam_bios_param	_set_ver(scsicam_bios_param, 3d965248)
-#define scsi_init_malloc	_set_ver(scsi_init_malloc, e5167cbc)
-#define scsi_init_free	_set_ver(scsi_init_free, 8b2721f8)
-#define print_command	_set_ver(print_command, 6f14cd75)
+#define __down	_set_ver(__down, b5922a9a)
+#define msdos_bmap	_set_ver(msdos_bmap, 520d840e)
+#define msdos_create	_set_ver(msdos_create, 5753a0d3)
+#define msdos_file_read	_set_ver(msdos_file_read, 68fc71a6)
+#define msdos_file_write	_set_ver(msdos_file_write, e605cffe)
+#define msdos_lookup	_set_ver(msdos_lookup, 34bc2266)
+#define msdos_mkdir	_set_ver(msdos_mkdir, 4a2f361b)
+#define msdos_mmap	_set_ver(msdos_mmap, 9f485336)
+#define msdos_put_inode	_set_ver(msdos_put_inode, 568bf471)
+#define msdos_put_super	_set_ver(msdos_put_super, 01384e55)
+#define msdos_read_inode	_set_ver(msdos_read_inode, 49d40a88)
+#define msdos_read_super	_set_ver(msdos_read_super, fc2dee0a)
+#define msdos_readdir	_set_ver(msdos_readdir, ada9e2e3)
+#define msdos_rename	_set_ver(msdos_rename, 10062231)
+#define msdos_rmdir	_set_ver(msdos_rmdir, d15c569d)
+#define msdos_smap	_set_ver(msdos_smap, c6d9a5c2)
+#define msdos_statfs	_set_ver(msdos_statfs, 27cd5a07)
+#define msdos_truncate	_set_ver(msdos_truncate, 0edfaa26)
+#define msdos_unlink	_set_ver(msdos_unlink, 01be559e)
+#define msdos_unlink_umsdos	_set_ver(msdos_unlink_umsdos, ab42df7e)
+#define msdos_write_inode	_set_ver(msdos_write_inode, 6b079e67)
+#define proc_register	_set_ver(proc_register, ce81e405)
+#define proc_unregister	_set_ver(proc_unregister, 18dcdd52)
+#define in_group_p	_set_ver(in_group_p, 2c50bc06)
+#define generate_cluster	_set_ver(generate_cluster, def8ac51)
+#define outswb	_set_ver(outswb, 5d4e1ee1)
+#define inswb	_set_ver(inswb, 63acc3df)
+#define ecard_find	_set_ver(ecard_find, 05fca99d)
+#define ecard_readchunk	_set_ver(ecard_readchunk, a2109de4)
+#define udelay	_set_ver(udelay, c0235c94)
+#define print_Scsi_Cmnd	_set_ver(print_Scsi_Cmnd, 8fcd5161)
 #define print_msg	_set_ver(print_msg, 0465f877)
-#define print_status	_set_ver(print_status, 32f84646)
-#define set_writetime	_set_ver(set_writetime, 52131916)
-#define sys_tz	_set_ver(sys_tz, aa3c9782)
-#define __wait_on_super	_set_ver(__wait_on_super, 61a5c00a)
-#define file_fsync	_set_ver(file_fsync, d30a190f)
-#define clear_inode	_set_ver(clear_inode, da2b0e9f)
-#define refile_buffer	_set_ver(refile_buffer, 8c69e123)
-#define ___strtok	_set_ver(___strtok, 8b55d69c)
-#define init_fifo	_set_ver(init_fifo, 082629c7)
-#define super_blocks	_set_ver(super_blocks, e1f1ee99)
-#define chrdev_inode_operations	_set_ver(chrdev_inode_operations, 6ba1faa3)
-#define blkdev_inode_operations	_set_ver(blkdev_inode_operations, ed443696)
-#define read_ahead	_set_ver(read_ahead, bbcd3768)
-#define get_hash_table	_set_ver(get_hash_table, 3b5f3c55)
-#define get_empty_inode	_set_ver(get_empty_inode, 554bdc75)
-#define insert_inode_hash	_set_ver(insert_inode_hash, 59b8c371)
-#define event	_set_ver(event, a6aac9c1)
-#define __down	_set_ver(__down, 75aa9e96)
 #endif /* _KSYMS_VER_ */
 #endif /* CONFIG_MODVERSIONS !__GENKSYMS__ */
diff -urNwbB linux/kernel/sched.c linux.arm/kernel/sched.c
--- linux/kernel/sched.c	Sun Mar  3 11:39:06 1996
+++ linux.arm/kernel/sched.c	Sun Feb 11 09:33:04 1996
@@ -33,6 +33,10 @@
 #include <asm/segment.h>
 #include <asm/pgtable.h>
 
+#ifdef __arm__
+#define builtin_return_address(x) bir
+#endif
+
 #include <linux/timex.h>
 
 /*
@@ -301,6 +305,12 @@
 {
 	struct wait_queue *tmp;
 	struct task_struct * p;
+#if defined (__arm2__) || defined (__arm3__)
+	void *bir;
+	__asm__ ("mov %0, lr\n\t": "=r" (bir));
+	if ((int)q & 0xfc000003) printk("wake_up: q = %p (PC=%p)\n", q, bir);
+	if ((int)*q & 0xfc000003) printk("wake_up: *q = %p (PC=%p)\n", q, bir);
+#endif
 
 	if (!q || !(tmp = *q))
 		return;
@@ -326,6 +336,10 @@
 {
 	struct wait_queue *tmp;
 	struct task_struct * p;
+#ifdef __arm__
+	void *bir;
+	__asm__ ("mov\t%0, lr\n\t": "=r" (bir));
+#endif
 
 	if (!q || !(tmp = *q))
 		return;
@@ -392,12 +406,18 @@
  * and the sorting routine counts on this..
  */
 static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
+#ifndef __arm__
 #define SLOW_BUT_DEBUGGING_TIMERS 1
+#endif
 
 void add_timer(struct timer_list * timer)
 {
 	unsigned long flags;
 	struct timer_list *p;
+#ifdef __arm__
+	void *bir;
+	__asm__ ("mov\t%0, lr\n\t": "=r" (bir));
+#endif
 
 #if SLOW_BUT_DEBUGGING_TIMERS
 	if (timer->next || timer->prev) {
@@ -412,11 +432,45 @@
 	do {
 		p = p->next;
 	} while (timer->expires > p->expires);
+#if defined (__arm2__) || defined (__arm3__)
+	if ((int)p & 3 || (int)p < 0x01800000)
+	{
+		printk("add_timer: p = %p\n", p);
+		goto bad_timer;
+	}
+#endif
 	timer->next = p;
+#if defined (__arm2__) || defined (__arm3__)
+	if ((int)p->prev & 3 || (int)p->prev < 0x01800000)
+	{
+		printk("add_timer: p(%p)->prev = %p\n", p, p->prev);
+		goto bad_timer;
+	}
+#endif
 	timer->prev = p->prev;
 	p->prev = timer;
 	timer->prev->next = timer;
 	restore_flags(flags);
+#if defined (__arm2__) || defined (__arm3__)
+	return;
+bad_timer:
+	p = &timer_head;
+	printk ("Timer list: ");
+	{
+		struct timer_list *op = p->prev;
+		do {
+			printk ("%p ", p);
+			if ((int)p & 3 || (int)p < 0x01800000)
+				break;
+			if (p->prev != op)
+				printk ("(bad prev) ");
+			if (p->next == p)
+				printk ("(bad next) ");
+			op = p;
+		} while ((p = p->next) != &timer_head && p != p->next);
+	}
+	panic ("\nwhile adding %p (PC=%p)", timer, bir);
+#endif
 }
 
 int del_timer(struct timer_list * timer)
@@ -424,6 +478,10 @@
 	unsigned long flags;
 #if SLOW_BUT_DEBUGGING_TIMERS
 	struct timer_list * p;
+#ifdef __arm__
+	void *bir;
+	__asm__ ("mov\t%0, lr\n\t": "=r" (bir));
+#endif
 
 	p = &timer_head;
 	save_flags(flags);
@@ -873,7 +931,14 @@
 	else
 		printk(" %016lx ", thread_saved_pc(&p->tss));
 #endif
+	/*
+	 * good idea, but not if you have a very large page size!
+	 */
+#ifndef __arm__
 	for (free = 1; free < PAGE_SIZE/sizeof(long) ; free++) {
+#else
+	for (free = 1; free < 4096/sizeof(long) ; free++) {
+#endif
 		if (((unsigned long *)p->kernel_stack_page)[free])
 			break;
 	}
diff -urNwbB linux/mm/Makefile linux.arm/mm/Makefile
--- linux/mm/Makefile	Sun Mar  3 11:30:27 1996
+++ linux.arm/mm/Makefile	Sun Feb 11 09:33:04 1996
@@ -8,6 +8,10 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
+ifndef CONFIG_ARM
 O_OBJS	 := memory.o swap.o mmap.o filemap.o mprotect.o kmalloc.o vmalloc.o
+else
+O_OBJS	 := memory.o swap.o mmap.o filemap.o mprotect.o kmalloc-arm.o vmalloc.o
+endif
 
 include $(TOPDIR)/Rules.make
diff -urNwbB linux/mm/kmalloc-arm.c linux.arm/mm/kmalloc-arm.c
--- linux/mm/kmalloc-arm.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/mm/kmalloc-arm.c	Sun Feb 11 09:33:05 1996
@@ -0,0 +1,501 @@
+/*
+ *  linux/mm/kmalloc.c
+ *
+ *  Copyright (C) 1994, 1995, 1996 R.M.King, ???, Linus Torvalds
+ *
+ * RMK - 4/1/96
+ *   - Changed allocation sizes to suite requested sizes.  On i386,
+ *     and other 4k page machines, it should use a around 2/3 of the
+ *     memory that it used to.
+ *   - Allowed recursive mallocing for machines with rediculously
+ *     large page sizes to reduce memory wastage.
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+
+/*
+ *
+ * A study of the sizes requested of kmalloc in v1.3.35 shows the following pattern:
+ *
+ *			Req.		------- Allocd -------
+ * Size   Times req.	Total(B)	size	 Total	 Pages		Wastage
+ *   16		 7	  112		  32	   224	   1		  16
+ *   36		 3	  108		  64	   192	   1		  28
+ *   56		22	 1232		  64	  1408	   1		   8
+ *   68		 3	  204		 128	   384	   1		  60
+ *  128		26	 3328		 256	  6656	   2		   0
+ *  512		 5	 2560		1020	  5100	   2		 508
+ *  516		 4	 2064		1020	  4080	   1		 504
+ *  752		 4	 3008		1020	  4080	   1		 268
+ * 1024		 1	 1024		2040	  1024	   1		   0
+ * 1060		 4	 4240		2040	  8160	   2		 980
+ * 2048		 2	 4096		4080	  8160	   2		2032
+ * 2956		 1	 2956		4080	  4080	   1		1124
+ * 4096		 8	32768		8176	 65408	  16		4080
+ * ---------------------------------------------------------------------------
+ * 	TOTALS		57700			108956	  32
+ *							(128k)
+ *
+ * On average, the old kmalloc uses twice the amount of memory required with a
+ * page size of 4k.  On 32k (which is what I'm dealing with):
+ *
+ *			Req.		------- Allocd -------
+ * Size   Times req.	Total(B)	size	 Total	 Pages		Wastage
+ *   16		 7	  112		  32	   224	   1		   8
+ *   36		 3	  108		  64	   192	   1		  20
+ *   56		22	 1232		  64	  1408	   1		   0
+ *   68		 3	  204		 128	   384	   1		  52
+ *  128		26	 3328		 256	  6656	   1		 120
+ *  512		 5	 2560		1020	  5100	   2		 500
+ *  516		 4	 2064		1020	  4080	   1		 496
+ *  752		 4	 3008		1020	  4080	   1		 260
+ * 1024		 1	 1024		2040	  1024	   1		   0
+ * 1060		 4	 4240		2040	  8160	   2		 972
+ * 2048		 2	 4096		4080	  8160	   2		2026
+ * 2956		 1	 2956		4080	  4080	   1		1116
+ * 4096		 8	32768		8176	 65408	   2		4072
+ * ---------------------------------------------------------------------------
+ * 	TOTALS		57700			108956	  17
+ *							(544k)
+ *
+ * Using the old kmalloc system, this ate up a lot of memory in the rounding up of the
+ * sizes.
+ * We now use a similar strategy to the old kmalloc, except we allocate in sizes determined
+ * by looking at the use of kmalloc.  There is an overhead of 2 words on each malloc for
+ * mallocs private data, and 4 words at the beginning of the page/4k block.
+ *
+ *			Req.		------ Allocd --(pages)
+ * Size	  Times req.	Total(B)	size	total	4k  32k		wastage
+ *   16		 7	  112		  24	  168	 1 		   0
+ *   36		 3	  108		  48	  144	 1		   0
+ *   56		22	 1232		  64	 1408	 1		   0
+ *   68		 3	  204		  80	  240	 1		   4
+ *  128		26	 3328		 136	 3536	 1		   0
+ *  512		 5	 2560		 580	 2900	 1   2		  60
+ *  516		 4	 2064		 580	 2320	 1		  56
+ *  752		 4	 3008		 816	 3264	 1		  56
+ * 1024		 1	 1024		1360	 1360	 1		 328
+ * 1060		 4	 4240		1360	 5440	 2		2048
+ * 2048		 2	 4096		4096	 8192	 2		1140
+ * 2956		 1	 2956		4096	 4096	 1		  20
+ * 4096		 8	32768		4676	37408	10   2		 582
+ * --------------------------------------------------------------------------
+ *	TOTALS		57700			70476	24   4
+ *						      (96k) (128k)
+ *
+ */
+
+#define MF_USED	 0xffaa0055
+#define MF_DMA   0xff00aa55
+#define MF_FREE  0x0055ffaa
+#define MF_INUSE 0x00ff55aa
+
+#ifdef __arm__
+#define PAGE_MALLOC 4096
+#define BIR_DEFS \
+    void *__bir; \
+    __asm__ __volatile__ ("mov %0, lr\n\t": "=r" (__bir));
+#define __builtin_return_address(x) __bir
+#else
+#define BIR_DEFS
+#endif
+
+#undef DEBUG
+
+struct mblk_header {
+	unsigned long mb_flags;
+	union {
+		unsigned long vmb_size;
+		struct mblk_header *vmb_next;
+	} v;
+};
+#define mb_size v.vmb_size
+#define mb_next v.vmb_next
+
+struct page_header {
+	unsigned long       ph_flags;
+	struct page_header *ph_next;
+	struct mblk_header *ph_free;
+	unsigned char       ph_order;
+	unsigned char       ph_dma;
+	unsigned short      ph_nfree;
+};
+
+struct size_descriptor {
+	struct page_header *sd_firstfree;
+	struct page_header *sd_dmafree;
+	struct page_header *sd_used;
+	int sd_size;
+	int sd_blocks;
+
+	int sd_gfporder;
+};
+
+static unsigned long bytes_wanted, bytes_malloced, pages_malloced;
+/*
+ * These are more suited to the sizes that the kernel will claim.
+ * Note the two main ones for 56 and 128 bytes (64 and 136), which
+ * get the most use.  gfporder has special values:
+ *  -1 = don't get a free page, but malloc a small page.
+ */
+static struct size_descriptor sizes[32] = {
+	{ NULL, NULL, NULL,     24, 170, -1 }, /* was 32 - shrink so that we have more */
+#if 1
+	{ NULL, NULL, NULL,     48,  85, -1 }, /* was 64 - shrink so that we have more, however... */
+#endif
+	{ NULL, NULL, NULL,     64,  63, -1 }, /* contains 56 byte mallocs */
+#if 1
+	{ NULL, NULL, NULL,     80,  51. -1 }, /* contains 68 byte mallocs + room for expansion */
+#endif
+	{ NULL, NULL, NULL,    136,  30, -1 }, /* was 128 - swallow up 128B */
+	{ NULL, NULL, NULL,    580,   7, -1 }, /* was 512 - swallow up 516B and allow for expansion */
+	{ NULL, NULL, NULL,    816,   5, -1 }, /* was 1024 - we get an extra block */
+	{ NULL, NULL, NULL,   1360,   3, -1 }, /* was 2048 - we get an extra block */
+	{ NULL, NULL, NULL,   4096,   7, 0 }, /* we need this one for mallocing 4k */
+	{ NULL, NULL, NULL,   4676,   7, 0 },
+	{ NULL, NULL, NULL,   8188,   4, 0 },
+	{ NULL, NULL, NULL,  16368,   2, 0 },
+	{ NULL, NULL, NULL,  32752,   1, 0 },
+	{ NULL, NULL, NULL,  65520,   1, 1 },
+	{ NULL, NULL, NULL, 131056,   1, 2 },
+	{ NULL, NULL, NULL, 262128,   1, 3 },
+	{ NULL, NULL, NULL,      0,   0, 0 },
+};
+
+static int num_sizes = 32;
+
+static int get_order (size_t size)
+{
+	int order;
+
+	for (order = 0; sizes[order].sd_size; order ++)
+		if (size <= sizes[order].sd_size && sizes[order].sd_gfporder != -3)
+			return order;
+	return -1;
+}
+
+long kmalloc_init (long start_mem, long end_mem)
+{
+    int i, gfporder, errors = 0;
+    /*
+     * kmalloc_init is now a bit more intelligent.
+     *
+     * It now sets up the gfp orders.
+     */
+#ifndef PAGE_MALLOC
+    gfporder = 0;
+#else
+    gfporder = -1;
+#endif
+    pages_malloced = bytes_wanted = bytes_malloced = 0;
+	
+    for (i = 0; i < num_sizes && sizes[i].sd_size; i++) {
+	sizes[i].sd_firstfree = NULL;
+	sizes[i].sd_dmafree = NULL;
+	sizes[i].sd_used = NULL;
+	if (gfporder >= 0 && sizes[i].sd_size > (PAGE_SIZE << gfporder))
+	    gfporder += 1;
+#ifdef PAGE_MALLOC
+	if (gfporder < 0 && sizes[i].sd_size >= PAGE_MALLOC)
+	    gfporder += 1;
+	if (gfporder < 0)
+	    sizes[i].sd_blocks = (PAGE_MALLOC - sizeof(struct page_header)) / sizes[i].sd_size;
+	else
+#endif
+	    sizes[i].sd_blocks = ((PAGE_SIZE << gfporder) - sizeof(struct page_header)) /
+	    				sizes[i].sd_size;
+	sizes[i].sd_gfporder = gfporder;
+    }
+
+    for (i = 0; i < num_sizes && sizes[i].sd_size; i++) {
+#ifdef PAGE_MALLOC
+	if (sizes[i].sd_gfporder < 0) {
+	    if ((sizes[i].sd_size * sizes[i].sd_blocks + sizeof (struct page_header))
+		<= PAGE_MALLOC)
+	     	continue;
+	} else
+#endif
+	{
+	    if ((sizes[i].sd_size * sizes[i].sd_blocks + sizeof (struct page_header))
+		<= (PAGE_SIZE << sizes[i].sd_gfporder))
+		continue;
+	}
+	printk ("Cannot use order %d (size %d, blocks %d)\n", i, sizes[i].sd_size, sizes[i].sd_blocks);
+	errors ++;
+    }
+    if (errors)
+	panic ("This only happens when someone messes with kmalloc");
+
+    return start_mem;
+}
+
+/*
+ * kmalloc of any size.
+ *
+ * if size < PAGE_MALLOC, then when we get a PAGE_MALLOC size, we malloc a
+ * PAGE_MALLOC block and override the malloc header with our own.
+ */
+void *kmalloc (size_t size, int priority)
+{
+    int dma_flag, order, i;
+    unsigned long flags;
+    struct page_header *ph;
+    struct mblk_header *mb;
+    BIR_DEFS
+
+#ifdef DEBUG
+    printk (KERN_DEBUG "kmalloc of size %d: ", size);
+#endif
+
+    dma_flag = priority & GFP_DMA;
+    priority &= GFP_LEVEL_MASK;
+
+    /* Sanity check... */
+    if (intr_count && priority != GFP_ATOMIC) {
+	static int count = 0;
+	if (++count < 5)
+	    printk (KERN_ERR "kmalloc called non-atomically from interrupt %p\n",
+		__builtin_return_address(0));
+	priority = GFP_ATOMIC;
+    }
+
+    order = get_order (size + sizeof (struct mblk_header));
+    if (order < 0) {
+	printk (KERN_ERR "kmalloc of too large a block (%d bytes).\n", (int) size);
+	return NULL;
+    }
+
+    save_flags (flags);
+
+    while (1) {
+	cli ();
+	ph = dma_flag ? sizes[order].sd_dmafree : sizes[order].sd_firstfree;
+	if (ph) {
+	    if (ph->ph_flags == MF_INUSE && (mb = ph->ph_free) != NULL) {
+		if (mb->mb_flags == MF_FREE) {
+		    ph->ph_free = mb->mb_next;
+		    if (--ph->ph_nfree == 0) {
+			if (dma_flag)
+			    sizes[order].sd_dmafree = ph->ph_next;
+			else
+			    sizes[order].sd_firstfree = ph->ph_next;
+			ph->ph_next = sizes[order].sd_used;
+		        sizes[order].sd_used = ph;
+		    }
+		    mb->mb_flags = MF_USED;
+		    mb->mb_size = size;
+		    bytes_wanted += size;
+		    bytes_malloced += sizes[order].sd_size;
+		    restore_flags (flags);
+#ifdef DEBUG
+		    printk ("block at %p malloced\n", mb);
+#endif
+		    return mb + 1; /* increments past header */
+		}
+		printk (KERN_EMERG
+			"kmalloc: problem: block %p on freelist isn't free - discarding page\n", mb);
+		if (dma_flag)
+		    sizes[order].sd_dmafree = ph->ph_next;
+		else
+		    sizes[order].sd_firstfree = ph->ph_next;
+	    } else if (ph->ph_flags != MF_INUSE)
+		panic ("kmalloc: problem: page %p on freelist isn't real\n", ph);
+	    else {
+	        printk (KERN_EMERG
+			"kmalloc: problem: page %p has null free pointer - discarding page\n", ph);
+		if (dma_flag)
+		    sizes[order].sd_dmafree = ph->ph_next;
+		else
+		    sizes[order].sd_firstfree = ph->ph_next;
+	    }
+	}
+	restore_flags (flags);
+	/* We need to get a new 4k page.  Whether we get a new page or malloc 4k depends on
+	 * the page size
+	 */
+#ifdef PAGE_MALLOC
+	if (sizes[order].sd_gfporder < 0) { /* malloc it */
+#ifdef DEBUG
+	    printk ("new small page: ");
+#endif
+	    mb = kmalloc (PAGE_MALLOC - sizeof (struct mblk_header), priority | dma_flag);
+	    /*
+	     * override malloc header with our own.  This means that we
+	     * destroy the size entry.  However, we change the flags entry
+	     * so that a free won't free it without the blocks inside it
+	     * are freed, and the data put back as it was.
+	     */
+	    if (mb)
+		ph = (struct page_header *) (mb - 1);
+	    else
+		ph = NULL;
+#ifdef DEBUG
+	    printk (KERN_DEBUG);
+#endif
+	} else
+#endif
+	{
+	    unsigned long max_addr;
+
+	    max_addr = dma_flag ? MAX_DMA_ADDRESS : ~0UL;
+#ifdef DEBUG
+	    printk ("new large page\n");
+#endif
+	    ph = (struct page_header *) __get_free_pages (priority, sizes[order].sd_gfporder, max_addr);
+	    if (ph)
+		pages_malloced += PAGE_SIZE;
+	}
+#ifdef DEBUG
+	printk ("kmalloc: at %p ", ph);
+#endif
+
+	if (!ph) {
+	    static unsigned long last = 0;
+	    if (priority != GFP_BUFFER && (last + 10*HZ) < jiffies) {
+		last = jiffies;
+		printk ("\n" KERN_CRIT "kmalloc: couldn't get a free page.....\n");
+	    }
+	    return NULL;
+	}
+	ph->ph_flags = MF_INUSE;
+	ph->ph_order = order;
+	ph->ph_nfree = sizes[order].sd_blocks;
+	ph->ph_dma   = dma_flag;
+
+	for (i = sizes[order].sd_blocks, mb = (struct mblk_header *)(ph + 1); i  > 1;
+				i --, mb = mb->mb_next) {
+	    mb->mb_flags = MF_FREE;
+	    mb->mb_next = (struct mblk_header *) (((unsigned long)mb) + sizes[order].sd_size);
+	}
+	ph->ph_free = (struct mblk_header *)(ph + 1);
+	mb->mb_flags = MF_FREE;
+	mb->mb_next = NULL;
+
+	cli();
+	if (dma_flag) {
+	    ph->ph_next = sizes[order].sd_dmafree;
+	    sizes[order].sd_dmafree = ph;
+	} else {
+	    ph->ph_next = sizes[order].sd_firstfree;
+	    sizes[order].sd_firstfree = ph;
+	}
+    }
+}
+
+void kfree (void *ptr)
+{
+    int order, size;
+    unsigned long flags;
+    struct page_header *ph;
+    struct mblk_header *mb;
+
+    mb = ((struct mblk_header *)ptr) - 1;
+
+    if (mb->mb_flags != MF_USED) {
+	printk (KERN_ERR "kfree of non-kmalloc'd memory: %p\n", ptr);
+	return;
+    }
+    size = mb->mb_size;
+    order = get_order (size + sizeof(struct mblk_header));
+
+    if (order < 0) {
+	printk (KERN_ERR "kfree of non-kmalloc'd memory: %p, size %d, order %d\n", ptr, size, order);
+	return;
+    }
+
+#ifdef PAGE_MALLOC
+    if (sizes[order].sd_gfporder < 0)
+	ph = (struct page_header *) ((((unsigned long) mb) & ~(PAGE_MALLOC - 1)) +
+		sizeof (struct page_header));
+    else
+#endif
+	ph = (struct page_header *) (((unsigned long) mb) & PAGE_MASK);
+#ifdef DEBUG
+    printk (KERN_DEBUG "kfree: page starts at %p\n", ph);
+#endif
+
+    if (ph->ph_flags != MF_INUSE && ph->ph_order != order) {
+	printk (KERN_ERR "kfree of non-kmalloc'd memory: %p, size %d, order %d\n", ptr, size, order);
+	return;
+    }
+
+    mb->mb_flags = MF_FREE;
+    save_flags (flags);
+    cli ();
+    bytes_wanted -= size;
+    bytes_malloced -= sizes[order].sd_size;
+    mb->mb_next = ph->ph_free;
+    ph->ph_free = mb;
+
+    if (++ph->ph_nfree == 1) {
+	/*
+	 * Page went from full to one free block: put it on the free list.
+	 */
+	struct page_header *pp;
+
+	if (sizes[order].sd_used == ph)
+	    sizes[order].sd_used = ph->ph_next;
+	else {
+	    for (pp = sizes[order].sd_used; pp != NULL && pp->ph_next != ph; pp = pp->ph_next);
+
+	    if (pp->ph_next == ph)
+		pp->ph_next = ph->ph_next;
+	    else {
+		printk (KERN_ERR "kfree: page %p not found on used list\n", ph);
+		restore_flags (flags);
+		return;
+	    }
+	}
+
+	ph->ph_next = sizes[order].sd_firstfree;
+	sizes[order].sd_firstfree = ph;
+    }
+
+    if (ph->ph_nfree == sizes[order].sd_blocks) {
+	if (sizes[order].sd_firstfree == ph)
+	    sizes[order].sd_firstfree = ph->ph_next;
+	else if (sizes[order].sd_dmafree == ph)
+	    sizes[order].sd_dmafree = ph->ph_next;
+	else {
+	    struct page_header *pp;
+
+	    for (pp = sizes[order].sd_firstfree; pp != NULL && pp->ph_next != ph; pp = pp->ph_next);
+
+	    if (pp == NULL)
+		for (pp = sizes[order].sd_dmafree; pp != NULL && pp->ph_next != ph; pp = pp->ph_next);
+
+	    if (pp)
+		pp->ph_next = ph->ph_next;
+	    else
+		printk (KERN_ERR "Oops.  Page %p not found on free list\n", ph);
+	}
+	restore_flags (flags);
+
+#ifdef PAGE_MALLOC
+	if (sizes[order].sd_gfporder < 0) {
+	    mb = (struct mblk_header *)ph;
+	    mb->mb_flags = MF_USED;
+	    mb->mb_size = PAGE_MALLOC - sizeof (struct mblk_header);
+	    kfree (mb + 1);
+	} else
+#endif
+	{
+	    pages_malloced -= PAGE_SIZE;
+	    free_pages ((unsigned long)ph, sizes[order].sd_gfporder);
+	}
+    }
+}
+
+
+#ifdef __arm__
+void kmalloc_stats (void)
+{
+    printk ("kmalloc usage: %ld bytes requested of malloc, %ld actually malloced, %ld bytes claimed\n", bytes_wanted,
+    		bytes_malloced, pages_malloced);
+}
+#endif
+
diff -urNwbB linux/mm/memory.c linux.arm/mm/memory.c
--- linux/mm/memory.c	Sun Mar  3 11:37:44 1996
+++ linux.arm/mm/memory.c	Sun Feb 11 09:33:05 1996
@@ -127,6 +127,11 @@
 	if (!(new_pg = pgd_alloc()))
 		return -ENOMEM;
 	page_dir = pgd_offset(&init_mm, 0);
+#ifdef __arm__
+	/* on arm, *must* have page 0 mapped in
+	 */
+	new_pg[0] = page_dir[0];
+#endif
 	for (i = USER_PTRS_PER_PGD ; i < PTRS_PER_PGD ; i++)
 		new_pg[i] = page_dir[i];
 	SET_PAGE_DIR(tsk, new_pg);
@@ -165,7 +170,11 @@
 		printk("%s trying to clear kernel page-directory: not good\n", tsk->comm);
 		return;
 	}
+#ifndef __arm__
 	for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
+#else
+	for (i = 1 ; i < USER_PTRS_PER_PGD ; i++) /* never remove 1st page on arm (for vectors) */
+#endif
 		free_one_pgd(page_dir + i);
 	invalidate();
 	return;
diff -urNwbB linux/mm/swap.c linux.arm/mm/swap.c
--- linux/mm/swap.c	Sun Mar  3 11:41:45 1996
+++ linux.arm/mm/swap.c	Thu Feb 22 23:30:57 1996
@@ -11,6 +11,10 @@
  * Swap aging added 23.2.95, Stephen Tweedie.
  */
 
+#ifdef __arm__
+#define __builtin_return_address(x) bir
+#endif
+
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/head.h>
@@ -205,6 +209,22 @@
 		kstat.pswpin++;
 	else
 		kstat.pswpout++;
+#if defined(__arm__) && defined (__arm3__)
+	if (rw == WRITE) {
+		extern void arm3_flushcache (void);
+		/*
+		 * Seeing as we might be swapping out some memory from
+		 * physical addresses, and it is cached, and mapped into
+		 * logical addresses and cached there as well, we *must*
+		 * flush the cache so that we read the page correctly.
+		 * No need to do this when we read, since the page has
+		 * to be put into logical mem, which requires invalidating
+		 * the memory map, which in turn flushes the cache.
+		 */
+		arm3_flushcache ();
+	}
+#endif
+	
 	if (p->swap_device) {
 		ll_rw_page(rw,p->swap_device,offset,buf);
 	} else if (p->swap_file) {
@@ -770,6 +790,16 @@
 
 void free_pages(unsigned long addr, unsigned long order)
 {
+#ifdef __arm__
+	void *bir;
+	__asm__ ("ldr\t%0, [ip,#-8]\n\t": "=r" (bir));
+	if (addr == 0)
+		return;
+	if (addr < LOGICAL_END) {
+		printk("free_pages: trying to free logical memory %08lx (PC=%p)\n", addr, bir);
+		return;
+	}
+#endif
 	if (MAP_NR(addr) < MAP_NR(high_memory)) {
 		unsigned long flag;
 		mem_map_t * map = mem_map + MAP_NR(addr);
@@ -836,6 +866,10 @@
 {
 	unsigned long flags;
 	int reserved_pages;
+#ifdef __arm__
+	void *bir;
+	asm ("ldr\t%0,[ip,#-8]\n\t": "=r" (bir));
+#endif
 
 	if (intr_count && priority != GFP_ATOMIC) {
 		static int count = 0;
@@ -889,6 +923,9 @@
 #ifdef SWAP_CACHE_INFO
 	show_swap_cache_info();
 #endif	
+#ifdef __arm__
+	kmalloc_stats ();
+#endif
 }
 
 /*
@@ -1315,8 +1352,10 @@
 	 * with a minimum of 16 pages. This is totally arbitrary
 	 */
 	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+6);
+#ifndef __arm__
 	if (i < 16)
 		i = 16;
+#endif
 	min_free_pages = i;
 	start_mem = init_swap_cache(start_mem, end_mem);
 	mem_map = (mem_map_t *) start_mem;
diff -urNwbB linux/mm/vmalloc.c linux.arm/mm/vmalloc.c
--- linux/mm/vmalloc.c	Sun Mar  3 11:37:13 1996
+++ linux.arm/mm/vmalloc.c	Sun Feb 11 09:33:05 1996
@@ -63,6 +63,9 @@
 		if (pte_none(page))
 			continue;
 		if (pte_present(page)) {
+#ifdef __arm__
+			mem_map[MAP_NR(pte_page(page))].reserved = 0;
+#endif
 			free_page(pte_page(page));
 			continue;
 		}
@@ -102,6 +105,9 @@
 	dir = pgd_offset(&init_mm, address);
 	while (address < end) {
 		free_area_pmd(dir, address, end - address);
+#ifdef __arm__
+		set_pgdir (address, *dir);
+#endif
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	}
@@ -123,6 +129,9 @@
 		page = __get_free_page(GFP_KERNEL);
 		if (!page)
 			return -ENOMEM;
+#ifdef __arm__
+		mem_map[MAP_NR(page)].reserved = 1;
+#endif
 		set_pte(pte, mk_pte(page, PAGE_KERNEL));
 		address += PAGE_SIZE;
 		pte++;
@@ -266,7 +275,11 @@
 	for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
 		if (tmp->addr == addr) {
 			*p = tmp->next;
+#ifndef __arm__
 			free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
+#else
+			free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size - PAGE_SIZE);
+#endif
 			kfree(tmp);
 			return;
 		}
diff -urNwbB linux/net/ethernet/eth.c linux.arm/net/ethernet/eth.c
--- linux/net/ethernet/eth.c	Sun Mar  3 11:40:42 1996
+++ linux.arm/net/ethernet/eth.c	Wed Feb 28 23:08:11 1996
@@ -197,7 +197,7 @@
 			skb->pkt_type=PACKET_OTHERHOST;
 	}
 	
-	if (ntohs(eth->h_proto) >= 1536)
+	if ((unsigned short)ntohs(eth->h_proto) >= 1536)
 		return eth->h_proto;
 		
 	rawp = skb->data;
diff -urNwbB linux/net/ipv4/rarp.c linux.arm/net/ipv4/rarp.c
--- linux/net/ipv4/rarp.c	Sun Mar  3 11:30:55 1996
+++ linux.arm/net/ipv4/rarp.c	Thu Feb 29 13:17:39 1996
@@ -259,7 +259,6 @@
 /*
  *	Process entry. Use tha for table lookup according to RFC903.
  */
-  
 	cli();
 	for (entry = rarp_tables; entry != NULL; entry = entry->next)
 		if (!memcmp(entry->ha, tha, rarp->ar_hln))
diff -urNwbB linux/net/ipv4/raw.c linux.arm/net/ipv4/raw.c
--- linux/net/ipv4/raw.c	Sun Mar  3 11:38:21 1996
+++ linux.arm/net/ipv4/raw.c	Sun Feb 11 09:33:06 1996
@@ -229,7 +229,13 @@
 	{
 		if(len>65535-sizeof(struct iphdr))
 			return -EMSGSIZE;
-		err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port);
+/*
+ * rmk: this doesn't make sense?  Why should we use sin.sin_port for the protocol when we
+ * asked socket for a specific proto?
+ *
+ *		err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port);
+ */
+		err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sk->protocol);
 	}
 	return err<0?err:len;
 }
diff -urNwbB linux/net/ipv4/tcp.c linux.arm/net/ipv4/tcp.c
--- linux/net/ipv4/tcp.c	Sun Mar  3 11:39:11 1996
+++ linux.arm/net/ipv4/tcp.c	Tue Feb 13 22:24:02 1996
@@ -1610,7 +1610,7 @@
   	t1->doff = sizeof(*t1)/4;
   	tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);
   	if (sk->debug)
-  		 printk("\rtcp_ack: seq %x ack %x\n", sequence, ack);
+  		 printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack);
   	tcp_statistics.TcpOutSegs++;
   	sk->prot->queue_xmit(sk, dev, buff, 1);
 }
@@ -3367,7 +3367,7 @@
 	if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq)) 
 	{
 		if(sk->debug)
-			printk("Ack ignored %u %u\n",ack,sk->sent_seq);
+			printk("Ack ignored %lu %lu\n",ack,sk->sent_seq);
 			
 		/*
 		 *	Keepalive processing.
@@ -3792,7 +3792,7 @@
 		if (!sk->dead)
 			sk->state_change(sk);
 		if(sk->debug)
-			printk("rcv_ack_seq: %X==%X, acked_seq: %X==%X\n",
+			printk("rcv_ack_seq: %lX==%lX, acked_seq: %lX==%lX\n",
 				sk->rcv_ack_seq,sk->write_seq,sk->acked_seq,sk->fin_seq);
 		if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/) 
 		{
@@ -4075,7 +4075,7 @@
 			if(after(new_seq,shut_seq))
 			{
 				if(sk->debug)
-					printk("Data arrived on %p after close [Data right edge %X, Socket shut on %X] %d\n",
+					printk("Data arrived on %p after close [Data right edge %lX, Socket shut on %lX] %d\n",
 						sk, new_seq, shut_seq, sk->blog);
 				if(sk->dead)
 				{
@@ -4119,9 +4119,9 @@
 			if(sk->debug)
 			{
 				printk("skb1=%p :", skb1);
-				printk("skb1->h.th->seq = %d: ", skb1->h.th->seq);
-				printk("skb->h.th->seq = %d\n",skb->h.th->seq);
-				printk("copied_seq = %d acked_seq = %d\n", sk->copied_seq,
+				printk("skb1->h.th->seq = %ld: ", skb1->h.th->seq);
+				printk("skb->h.th->seq = %ld\n",skb->h.th->seq);
+				printk("copied_seq = %ld acked_seq = %ld\n", sk->copied_seq,
 						sk->acked_seq);
 			}
 			
diff -urNwbB linux/scripts/Configure linux.arm/scripts/Configure
--- linux/scripts/Configure	Sun Mar  3 11:42:22 1996
+++ linux.arm/scripts/Configure	Sun Feb 11 09:33:06 1996
@@ -292,9 +292,11 @@
 fi
 . $CONFIG_IN
 
+if [ x$CONFIG_ARM = "x" ]; then
 case "$CONFIG_SOUND" in
 	[YyMm] )	$MAKE -C drivers/sound config || exit 1 ;;
 esac
+fi
 
 rm -f .config.old
 if [ -f .config ]; then
